LCOV - code coverage report
Current view: top level - editor/libeditor - HTMLEditRules.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 4646 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 153 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "HTMLEditRules.h"
       8             : 
       9             : #include <stdlib.h>
      10             : 
      11             : #include "HTMLEditUtils.h"
      12             : #include "TextEditUtils.h"
      13             : #include "WSRunObject.h"
      14             : #include "mozilla/Assertions.h"
      15             : #include "mozilla/CSSEditUtils.h"
      16             : #include "mozilla/EditorUtils.h"
      17             : #include "mozilla/HTMLEditor.h"
      18             : #include "mozilla/MathAlgorithms.h"
      19             : #include "mozilla/Move.h"
      20             : #include "mozilla/Preferences.h"
      21             : #include "mozilla/UniquePtr.h"
      22             : #include "mozilla/Unused.h"
      23             : #include "mozilla/dom/Selection.h"
      24             : #include "mozilla/dom/Element.h"
      25             : #include "mozilla/OwningNonNull.h"
      26             : #include "mozilla/mozalloc.h"
      27             : #include "nsAString.h"
      28             : #include "nsAlgorithm.h"
      29             : #include "nsCRT.h"
      30             : #include "nsCRTGlue.h"
      31             : #include "nsComponentManagerUtils.h"
      32             : #include "nsContentUtils.h"
      33             : #include "nsDebug.h"
      34             : #include "nsError.h"
      35             : #include "nsGkAtoms.h"
      36             : #include "nsIAtom.h"
      37             : #include "nsIContent.h"
      38             : #include "nsIContentIterator.h"
      39             : #include "nsID.h"
      40             : #include "nsIDOMCharacterData.h"
      41             : #include "nsIDOMDocument.h"
      42             : #include "nsIDOMElement.h"
      43             : #include "nsIDOMNode.h"
      44             : #include "nsIFrame.h"
      45             : #include "nsIHTMLAbsPosEditor.h"
      46             : #include "nsIHTMLDocument.h"
      47             : #include "nsINode.h"
      48             : #include "nsLiteralString.h"
      49             : #include "nsRange.h"
      50             : #include "nsReadableUtils.h"
      51             : #include "nsString.h"
      52             : #include "nsStringFwd.h"
      53             : #include "nsTArray.h"
      54             : #include "nsTextNode.h"
      55             : #include "nsThreadUtils.h"
      56             : #include "nsUnicharUtils.h"
      57             : #include <algorithm>
      58             : 
      59             : // Workaround for windows headers
      60             : #ifdef SetProp
      61             : #undef SetProp
      62             : #endif
      63             : 
      64             : class nsISupports;
      65             : 
      66             : namespace mozilla {
      67             : 
      68             : class RulesInfo;
      69             : 
      70             : using namespace dom;
      71             : 
      72             : //const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE";
      73             : //const static char* kMOZEditorBogusNodeValue="TRUE";
      74             : 
      75             : enum
      76             : {
      77             :   kLonely = 0,
      78             :   kPrevSib = 1,
      79             :   kNextSib = 2,
      80             :   kBothSibs = 3
      81             : };
      82             : 
      83             : /********************************************************
      84             :  *  first some helpful functors we will use
      85             :  ********************************************************/
      86             : 
      87           0 : static bool IsBlockNode(const nsINode& node)
      88             : {
      89           0 :   return HTMLEditor::NodeIsBlockStatic(&node);
      90             : }
      91             : 
      92           0 : static bool IsInlineNode(const nsINode& node)
      93             : {
      94           0 :   return !IsBlockNode(node);
      95             : }
      96             : 
      97             : static bool
      98           0 : IsStyleCachePreservingAction(EditAction action)
      99             : {
     100           0 :   return action == EditAction::deleteSelection ||
     101           0 :          action == EditAction::insertBreak ||
     102           0 :          action == EditAction::makeList ||
     103           0 :          action == EditAction::indent ||
     104           0 :          action == EditAction::outdent ||
     105           0 :          action == EditAction::align ||
     106           0 :          action == EditAction::makeBasicBlock ||
     107           0 :          action == EditAction::removeList ||
     108           0 :          action == EditAction::makeDefListItem ||
     109           0 :          action == EditAction::insertElement ||
     110           0 :          action == EditAction::insertQuotation;
     111             : }
     112             : 
     113             : static nsIAtom&
     114           0 : ParagraphSeparatorElement(ParagraphSeparator separator)
     115             : {
     116           0 :   switch (separator) {
     117             :     default:
     118           0 :       MOZ_FALLTHROUGH_ASSERT("Unexpected paragraph separator!");
     119             : 
     120             :     case ParagraphSeparator::div:
     121           0 :       return *nsGkAtoms::div;
     122             : 
     123             :     case ParagraphSeparator::p:
     124           0 :       return *nsGkAtoms::p;
     125             : 
     126             :     case ParagraphSeparator::br:
     127           0 :       return *nsGkAtoms::br;
     128             :   }
     129             : }
     130             : 
     131             : class TableCellAndListItemFunctor final : public BoolDomIterFunctor
     132             : {
     133             : public:
     134             :   // Used to build list of all li's, td's & th's iterator covers
     135           0 :   virtual bool operator()(nsINode* aNode) const
     136             :   {
     137           0 :     return HTMLEditUtils::IsTableCell(aNode) ||
     138           0 :            HTMLEditUtils::IsListItem(aNode);
     139             :   }
     140             : };
     141             : 
     142             : class BRNodeFunctor final : public BoolDomIterFunctor
     143             : {
     144             : public:
     145           0 :   virtual bool operator()(nsINode* aNode) const
     146             :   {
     147           0 :     return aNode->IsHTMLElement(nsGkAtoms::br);
     148             :   }
     149             : };
     150             : 
     151             : class EmptyEditableFunctor final : public BoolDomIterFunctor
     152             : {
     153             : public:
     154           0 :   explicit EmptyEditableFunctor(HTMLEditor* aHTMLEditor)
     155           0 :     : mHTMLEditor(aHTMLEditor)
     156           0 :   {}
     157             : 
     158           0 :   virtual bool operator()(nsINode* aNode) const
     159             :   {
     160           0 :     if (mHTMLEditor->IsEditable(aNode) &&
     161           0 :         (HTMLEditUtils::IsListItem(aNode) ||
     162           0 :          HTMLEditUtils::IsTableCellOrCaption(*aNode))) {
     163             :       bool bIsEmptyNode;
     164             :       nsresult rv =
     165           0 :         mHTMLEditor->IsEmptyNode(aNode, &bIsEmptyNode, false, false);
     166           0 :       NS_ENSURE_SUCCESS(rv, false);
     167           0 :       if (bIsEmptyNode) {
     168           0 :         return true;
     169             :       }
     170             :     }
     171           0 :     return false;
     172             :   }
     173             : 
     174             : protected:
     175             :   HTMLEditor* mHTMLEditor;
     176             : };
     177             : 
     178             : /********************************************************
     179             :  * mozilla::HTMLEditRules
     180             :  ********************************************************/
     181             : 
     182           0 : HTMLEditRules::HTMLEditRules()
     183             :   : mHTMLEditor(nullptr)
     184             :   , mListenerEnabled(false)
     185             :   , mReturnInEmptyLIKillsList(false)
     186             :   , mDidDeleteSelection(false)
     187             :   , mDidRangedDelete(false)
     188             :   , mRestoreContentEditableCount(false)
     189           0 :   , mJoinOffset(0)
     190             : {
     191           0 :   InitFields();
     192           0 : }
     193             : 
     194             : void
     195           0 : HTMLEditRules::InitFields()
     196             : {
     197           0 :   mHTMLEditor = nullptr;
     198           0 :   mDocChangeRange = nullptr;
     199           0 :   mListenerEnabled = true;
     200           0 :   mReturnInEmptyLIKillsList = true;
     201           0 :   mDidDeleteSelection = false;
     202           0 :   mDidRangedDelete = false;
     203           0 :   mRestoreContentEditableCount = false;
     204           0 :   mUtilRange = nullptr;
     205           0 :   mJoinOffset = 0;
     206           0 :   mNewBlock = nullptr;
     207           0 :   mRangeItem = new RangeItem();
     208             : 
     209           0 :   InitStyleCacheArray(mCachedStyles);
     210           0 : }
     211             : 
     212             : void
     213           0 : HTMLEditRules::InitStyleCacheArray(StyleCache aStyleCache[SIZE_STYLE_TABLE])
     214             : {
     215           0 :   aStyleCache[0] = StyleCache(nsGkAtoms::b, EmptyString());
     216           0 :   aStyleCache[1] = StyleCache(nsGkAtoms::i, EmptyString());
     217           0 :   aStyleCache[2] = StyleCache(nsGkAtoms::u, EmptyString());
     218           0 :   aStyleCache[3] = StyleCache(nsGkAtoms::font, NS_LITERAL_STRING("face"));
     219           0 :   aStyleCache[4] = StyleCache(nsGkAtoms::font, NS_LITERAL_STRING("size"));
     220           0 :   aStyleCache[5] = StyleCache(nsGkAtoms::font, NS_LITERAL_STRING("color"));
     221           0 :   aStyleCache[6] = StyleCache(nsGkAtoms::tt, EmptyString());
     222           0 :   aStyleCache[7] = StyleCache(nsGkAtoms::em, EmptyString());
     223           0 :   aStyleCache[8] = StyleCache(nsGkAtoms::strong, EmptyString());
     224           0 :   aStyleCache[9] = StyleCache(nsGkAtoms::dfn, EmptyString());
     225           0 :   aStyleCache[10] = StyleCache(nsGkAtoms::code, EmptyString());
     226           0 :   aStyleCache[11] = StyleCache(nsGkAtoms::samp, EmptyString());
     227           0 :   aStyleCache[12] = StyleCache(nsGkAtoms::var, EmptyString());
     228           0 :   aStyleCache[13] = StyleCache(nsGkAtoms::cite, EmptyString());
     229           0 :   aStyleCache[14] = StyleCache(nsGkAtoms::abbr, EmptyString());
     230           0 :   aStyleCache[15] = StyleCache(nsGkAtoms::acronym, EmptyString());
     231           0 :   aStyleCache[16] = StyleCache(nsGkAtoms::backgroundColor, EmptyString());
     232           0 :   aStyleCache[17] = StyleCache(nsGkAtoms::sub, EmptyString());
     233           0 :   aStyleCache[18] = StyleCache(nsGkAtoms::sup, EmptyString());
     234           0 : }
     235             : 
     236           0 : HTMLEditRules::~HTMLEditRules()
     237             : {
     238             :   // remove ourselves as a listener to edit actions
     239             :   // In some cases, we have already been removed by
     240             :   // ~HTMLEditor, in which case we will get a null pointer here
     241             :   // which we ignore.  But this allows us to add the ability to
     242             :   // switch rule sets on the fly if we want.
     243           0 :   if (mHTMLEditor) {
     244           0 :     mHTMLEditor->RemoveEditActionListener(this);
     245             :   }
     246           0 : }
     247             : 
     248           0 : NS_IMPL_ADDREF_INHERITED(HTMLEditRules, TextEditRules)
     249           0 : NS_IMPL_RELEASE_INHERITED(HTMLEditRules, TextEditRules)
     250           0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLEditRules)
     251           0 :   NS_INTERFACE_TABLE_INHERITED(HTMLEditRules, nsIEditActionListener)
     252           0 : NS_INTERFACE_TABLE_TAIL_INHERITING(TextEditRules)
     253             : 
     254           0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLEditRules, TextEditRules,
     255             :                                    mDocChangeRange, mUtilRange, mNewBlock,
     256             :                                    mRangeItem)
     257             : 
     258             : NS_IMETHODIMP
     259           0 : HTMLEditRules::Init(TextEditor* aTextEditor)
     260             : {
     261           0 :   if (NS_WARN_IF(!aTextEditor)) {
     262           0 :     return NS_ERROR_INVALID_ARG;
     263             :   }
     264             : 
     265           0 :   InitFields();
     266             : 
     267           0 :   mHTMLEditor = aTextEditor->AsHTMLEditor();
     268           0 :   if (NS_WARN_IF(!mHTMLEditor)) {
     269           0 :     return NS_ERROR_INVALID_ARG;
     270             :   }
     271             : 
     272             :   // call through to base class Init
     273           0 :   nsresult rv = TextEditRules::Init(aTextEditor);
     274           0 :   NS_ENSURE_SUCCESS(rv, rv);
     275             : 
     276             :   // cache any prefs we care about
     277             :   static const char kPrefName[] =
     278             :     "editor.html.typing.returnInEmptyListItemClosesList";
     279             :   nsAdoptingCString returnInEmptyLIKillsList =
     280           0 :     Preferences::GetCString(kPrefName);
     281             : 
     282             :   // only when "false", becomes FALSE.  Otherwise (including empty), TRUE.
     283             :   // XXX Why was this pref designed as a string and not bool?
     284           0 :   mReturnInEmptyLIKillsList = !returnInEmptyLIKillsList.EqualsLiteral("false");
     285             : 
     286             :   // make a utility range for use by the listenter
     287           0 :   nsCOMPtr<nsINode> node = mHTMLEditor->GetRoot();
     288           0 :   if (!node) {
     289           0 :     node = mHTMLEditor->GetDocument();
     290             :   }
     291             : 
     292           0 :   NS_ENSURE_STATE(node);
     293             : 
     294           0 :   mUtilRange = new nsRange(node);
     295             : 
     296             :   // set up mDocChangeRange to be whole doc
     297             :   // temporarily turn off rules sniffing
     298           0 :   AutoLockRulesSniffing lockIt(this);
     299           0 :   if (!mDocChangeRange) {
     300           0 :     mDocChangeRange = new nsRange(node);
     301             :   }
     302             : 
     303           0 :   if (node->IsElement()) {
     304           0 :     ErrorResult rv;
     305           0 :     mDocChangeRange->SelectNode(*node, rv);
     306           0 :     NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
     307           0 :     AdjustSpecialBreaks();
     308             :   }
     309             : 
     310             :   // add ourselves as a listener to edit actions
     311           0 :   return mHTMLEditor->AddEditActionListener(this);
     312             : }
     313             : 
     314             : NS_IMETHODIMP
     315           0 : HTMLEditRules::DetachEditor()
     316             : {
     317           0 :   if (mHTMLEditor) {
     318           0 :     mHTMLEditor->RemoveEditActionListener(this);
     319             :   }
     320           0 :   mHTMLEditor = nullptr;
     321           0 :   return TextEditRules::DetachEditor();
     322             : }
     323             : 
     324             : NS_IMETHODIMP
     325           0 : HTMLEditRules::BeforeEdit(EditAction action,
     326             :                           nsIEditor::EDirection aDirection)
     327             : {
     328           0 :   if (mLockRulesSniffing) {
     329           0 :     return NS_OK;
     330             :   }
     331             : 
     332           0 :   NS_ENSURE_STATE(mHTMLEditor);
     333           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
     334             : 
     335           0 :   AutoLockRulesSniffing lockIt(this);
     336           0 :   mDidExplicitlySetInterline = false;
     337             : 
     338           0 :   if (!mActionNesting) {
     339           0 :     mActionNesting++;
     340             : 
     341             :     // Clear our flag about if just deleted a range
     342           0 :     mDidRangedDelete = false;
     343             : 
     344             :     // Remember where our selection was before edit action took place:
     345             : 
     346             :     // Get selection
     347           0 :     RefPtr<Selection> selection = htmlEditor->GetSelection();
     348             : 
     349             :     // Get the selection location
     350           0 :     if (NS_WARN_IF(!selection) || !selection->RangeCount()) {
     351           0 :       return NS_ERROR_UNEXPECTED;
     352             :     }
     353           0 :     mRangeItem->mStartContainer = selection->GetRangeAt(0)->GetStartContainer();
     354           0 :     mRangeItem->mStartOffset = selection->GetRangeAt(0)->StartOffset();
     355           0 :     mRangeItem->mEndContainer = selection->GetRangeAt(0)->GetEndContainer();
     356           0 :     mRangeItem->mEndOffset = selection->GetRangeAt(0)->EndOffset();
     357           0 :     nsCOMPtr<nsINode> selStartNode = mRangeItem->mStartContainer;
     358           0 :     nsCOMPtr<nsINode> selEndNode = mRangeItem->mEndContainer;
     359             : 
     360             :     // Register with range updater to track this as we perturb the doc
     361           0 :     htmlEditor->mRangeUpdater.RegisterRangeItem(mRangeItem);
     362             : 
     363             :     // Clear deletion state bool
     364           0 :     mDidDeleteSelection = false;
     365             : 
     366             :     // Clear out mDocChangeRange and mUtilRange
     367           0 :     if (mDocChangeRange) {
     368             :       // Clear out our accounting of what changed
     369           0 :       mDocChangeRange->Reset();
     370             :     }
     371           0 :     if (mUtilRange) {
     372             :       // Ditto for mUtilRange.
     373           0 :       mUtilRange->Reset();
     374             :     }
     375             : 
     376             :     // Remember current inline styles for deletion and normal insertion ops
     377           0 :     if (action == EditAction::insertText ||
     378           0 :         action == EditAction::insertIMEText ||
     379           0 :         action == EditAction::deleteSelection ||
     380           0 :         IsStyleCachePreservingAction(action)) {
     381             :       nsCOMPtr<nsINode> selNode =
     382           0 :         aDirection == nsIEditor::eNext ? selEndNode : selStartNode;
     383           0 :       nsresult rv = CacheInlineStyles(GetAsDOMNode(selNode));
     384           0 :       NS_ENSURE_SUCCESS(rv, rv);
     385             :     }
     386             : 
     387             :     // Stabilize the document against contenteditable count changes
     388           0 :     nsCOMPtr<nsIDOMDocument> doc = htmlEditor->GetDOMDocument();
     389           0 :     NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
     390           0 :     nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
     391           0 :     NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE);
     392           0 :     if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
     393           0 :       htmlDoc->ChangeContentEditableCount(nullptr, +1);
     394           0 :       mRestoreContentEditableCount = true;
     395             :     }
     396             : 
     397             :     // Check that selection is in subtree defined by body node
     398           0 :     ConfirmSelectionInBody();
     399             :     // Let rules remember the top level action
     400           0 :     mTheAction = action;
     401             :   }
     402           0 :   return NS_OK;
     403             : }
     404             : 
     405             : 
     406             : NS_IMETHODIMP
     407           0 : HTMLEditRules::AfterEdit(EditAction action,
     408             :                          nsIEditor::EDirection aDirection)
     409             : {
     410           0 :   if (mLockRulesSniffing) {
     411           0 :     return NS_OK;
     412             :   }
     413             : 
     414           0 :   NS_ENSURE_STATE(mHTMLEditor);
     415           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
     416             : 
     417           0 :   AutoLockRulesSniffing lockIt(this);
     418             : 
     419           0 :   MOZ_ASSERT(mActionNesting > 0);
     420           0 :   nsresult rv = NS_OK;
     421           0 :   mActionNesting--;
     422           0 :   if (!mActionNesting) {
     423             :     // Do all the tricky stuff
     424           0 :     rv = AfterEditInner(action, aDirection);
     425             : 
     426             :     // Free up selectionState range item
     427           0 :     htmlEditor->mRangeUpdater.DropRangeItem(mRangeItem);
     428             : 
     429             :     // Reset the contenteditable count to its previous value
     430           0 :     if (mRestoreContentEditableCount) {
     431           0 :       nsCOMPtr<nsIDOMDocument> doc = htmlEditor->GetDOMDocument();
     432           0 :       NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
     433           0 :       nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
     434           0 :       NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE);
     435           0 :       if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
     436           0 :         htmlDoc->ChangeContentEditableCount(nullptr, -1);
     437             :       }
     438           0 :       mRestoreContentEditableCount = false;
     439             :     }
     440             :   }
     441             : 
     442           0 :   NS_ENSURE_SUCCESS(rv, rv);
     443             : 
     444           0 :   return NS_OK;
     445             : }
     446             : 
     447             : nsresult
     448           0 : HTMLEditRules::AfterEditInner(EditAction action,
     449             :                               nsIEditor::EDirection aDirection)
     450             : {
     451           0 :   ConfirmSelectionInBody();
     452           0 :   if (action == EditAction::ignore) {
     453           0 :     return NS_OK;
     454             :   }
     455             : 
     456           0 :   NS_ENSURE_STATE(mHTMLEditor);
     457           0 :   RefPtr<Selection> selection = mHTMLEditor->GetSelection();
     458           0 :   NS_ENSURE_STATE(selection);
     459             : 
     460           0 :   nsCOMPtr<nsIDOMNode> rangeStartContainer, rangeEndContainer;
     461           0 :   int32_t rangeStartOffset = 0, rangeEndOffset = 0;
     462             :   // do we have a real range to act on?
     463           0 :   bool bDamagedRange = false;
     464           0 :   if (mDocChangeRange) {
     465           0 :     mDocChangeRange->GetStartContainer(getter_AddRefs(rangeStartContainer));
     466           0 :     mDocChangeRange->GetEndContainer(getter_AddRefs(rangeEndContainer));
     467           0 :     mDocChangeRange->GetStartOffset(&rangeStartOffset);
     468           0 :     mDocChangeRange->GetEndOffset(&rangeEndOffset);
     469           0 :     if (rangeStartContainer && rangeEndContainer) {
     470           0 :       bDamagedRange = true;
     471             :     }
     472             :   }
     473             : 
     474           0 :   if (bDamagedRange && !((action == EditAction::undo) ||
     475             :                          (action == EditAction::redo))) {
     476             :     // don't let any txns in here move the selection around behind our back.
     477             :     // Note that this won't prevent explicit selection setting from working.
     478           0 :     NS_ENSURE_STATE(mHTMLEditor);
     479           0 :     AutoTransactionsConserveSelection dontSpazMySelection(mHTMLEditor);
     480             : 
     481             :     // expand the "changed doc range" as needed
     482           0 :     PromoteRange(*mDocChangeRange, action);
     483             : 
     484             :     // if we did a ranged deletion or handling backspace key, make sure we have
     485             :     // a place to put caret.
     486             :     // Note we only want to do this if the overall operation was deletion,
     487             :     // not if deletion was done along the way for EditAction::loadHTML, EditAction::insertText, etc.
     488             :     // That's why this is here rather than DidDeleteSelection().
     489           0 :     if (action == EditAction::deleteSelection && mDidRangedDelete) {
     490           0 :       nsresult rv = InsertBRIfNeeded(selection);
     491           0 :       NS_ENSURE_SUCCESS(rv, rv);
     492             :     }
     493             : 
     494             :     // add in any needed <br>s, and remove any unneeded ones.
     495           0 :     AdjustSpecialBreaks();
     496             : 
     497             :     // merge any adjacent text nodes
     498           0 :     if (action != EditAction::insertText &&
     499             :         action != EditAction::insertIMEText) {
     500           0 :       NS_ENSURE_STATE(mHTMLEditor);
     501           0 :       nsresult rv = mHTMLEditor->CollapseAdjacentTextNodes(mDocChangeRange);
     502           0 :       NS_ENSURE_SUCCESS(rv, rv);
     503             :     }
     504             : 
     505             :     // clean up any empty nodes in the selection
     506           0 :     nsresult rv = RemoveEmptyNodes();
     507           0 :     NS_ENSURE_SUCCESS(rv, rv);
     508             : 
     509             :     // attempt to transform any unneeded nbsp's into spaces after doing various operations
     510           0 :     if (action == EditAction::insertText ||
     511           0 :         action == EditAction::insertIMEText ||
     512           0 :         action == EditAction::deleteSelection ||
     513           0 :         action == EditAction::insertBreak ||
     514           0 :         action == EditAction::htmlPaste ||
     515             :         action == EditAction::loadHTML) {
     516           0 :       rv = AdjustWhitespace(selection);
     517           0 :       NS_ENSURE_SUCCESS(rv, rv);
     518             : 
     519             :       // also do this for original selection endpoints.
     520           0 :       NS_ENSURE_STATE(mHTMLEditor);
     521           0 :       NS_ENSURE_STATE(mRangeItem->mStartContainer);
     522           0 :       NS_ENSURE_STATE(mRangeItem->mEndContainer);
     523           0 :       WSRunObject(mHTMLEditor, mRangeItem->mStartContainer,
     524           0 :                   mRangeItem->mStartOffset).AdjustWhitespace();
     525             :       // we only need to handle old selection endpoint if it was different from start
     526           0 :       if (mRangeItem->mStartContainer != mRangeItem->mEndContainer ||
     527           0 :           mRangeItem->mStartOffset != mRangeItem->mEndOffset) {
     528           0 :         NS_ENSURE_STATE(mHTMLEditor);
     529           0 :         WSRunObject(mHTMLEditor, mRangeItem->mEndContainer,
     530           0 :                     mRangeItem->mEndOffset).AdjustWhitespace();
     531             :       }
     532             :     }
     533             : 
     534             :     // if we created a new block, make sure selection lands in it
     535           0 :     if (mNewBlock) {
     536           0 :       rv = PinSelectionToNewBlock(selection);
     537           0 :       mNewBlock = nullptr;
     538             :     }
     539             : 
     540             :     // adjust selection for insert text, html paste, and delete actions
     541           0 :     if (action == EditAction::insertText ||
     542           0 :         action == EditAction::insertIMEText ||
     543           0 :         action == EditAction::deleteSelection ||
     544           0 :         action == EditAction::insertBreak ||
     545           0 :         action == EditAction::htmlPaste ||
     546             :         action == EditAction::loadHTML) {
     547           0 :       rv = AdjustSelection(selection, aDirection);
     548           0 :       NS_ENSURE_SUCCESS(rv, rv);
     549             :     }
     550             : 
     551             :     // check for any styles which were removed inappropriately
     552           0 :     if (action == EditAction::insertText ||
     553           0 :         action == EditAction::insertIMEText ||
     554           0 :         action == EditAction::deleteSelection ||
     555           0 :         IsStyleCachePreservingAction(action)) {
     556           0 :       NS_ENSURE_STATE(mHTMLEditor);
     557           0 :       mHTMLEditor->mTypeInState->UpdateSelState(selection);
     558           0 :       rv = ReapplyCachedStyles();
     559           0 :       NS_ENSURE_SUCCESS(rv, rv);
     560           0 :       ClearCachedStyles();
     561             :     }
     562             :   }
     563             : 
     564           0 :   NS_ENSURE_STATE(mHTMLEditor);
     565             : 
     566             :   nsresult rv =
     567           0 :     mHTMLEditor->HandleInlineSpellCheck(
     568             :                    action, selection,
     569           0 :                    GetAsDOMNode(mRangeItem->mStartContainer),
     570           0 :                    mRangeItem->mStartOffset,
     571             :                    rangeStartContainer, rangeStartOffset,
     572           0 :                    rangeEndContainer, rangeEndOffset);
     573           0 :   NS_ENSURE_SUCCESS(rv, rv);
     574             : 
     575             :   // detect empty doc
     576           0 :   rv = CreateBogusNodeIfNeeded(selection);
     577           0 :   NS_ENSURE_SUCCESS(rv, rv);
     578             : 
     579             :   // adjust selection HINT if needed
     580           0 :   if (!mDidExplicitlySetInterline) {
     581           0 :     CheckInterlinePosition(*selection);
     582             :   }
     583             : 
     584           0 :   return NS_OK;
     585             : }
     586             : 
     587             : NS_IMETHODIMP
     588           0 : HTMLEditRules::WillDoAction(Selection* aSelection,
     589             :                             RulesInfo* aInfo,
     590             :                             bool* aCancel,
     591             :                             bool* aHandled)
     592             : {
     593           0 :   MOZ_ASSERT(aInfo && aCancel && aHandled);
     594             : 
     595           0 :   *aCancel = false;
     596           0 :   *aHandled = false;
     597             : 
     598             :   // my kingdom for dynamic cast
     599           0 :   TextRulesInfo* info = static_cast<TextRulesInfo*>(aInfo);
     600             : 
     601             :   // Deal with actions for which we don't need to check whether the selection is
     602             :   // editable.
     603           0 :   if (info->action == EditAction::outputText ||
     604           0 :       info->action == EditAction::undo ||
     605           0 :       info->action == EditAction::redo) {
     606           0 :     return TextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
     607             :   }
     608             : 
     609             :   // Nothing to do if there's no selection to act on
     610           0 :   if (!aSelection) {
     611           0 :     return NS_OK;
     612             :   }
     613           0 :   NS_ENSURE_TRUE(aSelection->RangeCount(), NS_OK);
     614             : 
     615           0 :   RefPtr<nsRange> range = aSelection->GetRangeAt(0);
     616           0 :   nsCOMPtr<nsINode> selStartNode = range->GetStartContainer();
     617             : 
     618           0 :   NS_ENSURE_STATE(mHTMLEditor);
     619           0 :   if (!mHTMLEditor->IsModifiableNode(selStartNode)) {
     620           0 :     *aCancel = true;
     621           0 :     return NS_OK;
     622             :   }
     623             : 
     624           0 :   nsCOMPtr<nsINode> selEndNode = range->GetEndContainer();
     625             : 
     626           0 :   if (selStartNode != selEndNode) {
     627           0 :     NS_ENSURE_STATE(mHTMLEditor);
     628           0 :     if (!mHTMLEditor->IsModifiableNode(selEndNode)) {
     629           0 :       *aCancel = true;
     630           0 :       return NS_OK;
     631             :     }
     632             : 
     633           0 :     NS_ENSURE_STATE(mHTMLEditor);
     634           0 :     if (!mHTMLEditor->IsModifiableNode(range->GetCommonAncestor())) {
     635           0 :       *aCancel = true;
     636           0 :       return NS_OK;
     637             :     }
     638             :   }
     639             : 
     640           0 :   switch (info->action) {
     641             :     case EditAction::insertText:
     642             :     case EditAction::insertIMEText:
     643           0 :       UndefineCaretBidiLevel(aSelection);
     644           0 :       return WillInsertText(info->action, aSelection, aCancel, aHandled,
     645           0 :                             info->inString, info->outString, info->maxLength);
     646             :     case EditAction::loadHTML:
     647           0 :       return WillLoadHTML(aSelection, aCancel);
     648             :     case EditAction::insertBreak:
     649           0 :       UndefineCaretBidiLevel(aSelection);
     650           0 :       return WillInsertBreak(*aSelection, aCancel, aHandled);
     651             :     case EditAction::deleteSelection:
     652           0 :       return WillDeleteSelection(aSelection, info->collapsedAction,
     653           0 :                                  info->stripWrappers, aCancel, aHandled);
     654             :     case EditAction::makeList:
     655           0 :       return WillMakeList(aSelection, info->blockType, info->entireList,
     656           0 :                           info->bulletType, aCancel, aHandled);
     657             :     case EditAction::indent:
     658           0 :       return WillIndent(aSelection, aCancel, aHandled);
     659             :     case EditAction::outdent:
     660           0 :       return WillOutdent(*aSelection, aCancel, aHandled);
     661             :     case EditAction::setAbsolutePosition:
     662           0 :       return WillAbsolutePosition(*aSelection, aCancel, aHandled);
     663             :     case EditAction::removeAbsolutePosition:
     664           0 :       return WillRemoveAbsolutePosition(aSelection, aCancel, aHandled);
     665             :     case EditAction::align:
     666           0 :       return WillAlign(*aSelection, *info->alignType, aCancel, aHandled);
     667             :     case EditAction::makeBasicBlock:
     668           0 :       return WillMakeBasicBlock(*aSelection, *info->blockType, aCancel,
     669           0 :                                 aHandled);
     670             :     case EditAction::removeList:
     671           0 :       return WillRemoveList(aSelection, info->bOrdered, aCancel, aHandled);
     672             :     case EditAction::makeDefListItem:
     673           0 :       return WillMakeDefListItem(aSelection, info->blockType, info->entireList,
     674           0 :                                  aCancel, aHandled);
     675             :     case EditAction::insertElement:
     676           0 :       WillInsert(*aSelection, aCancel);
     677           0 :       return NS_OK;
     678             :     case EditAction::decreaseZIndex:
     679           0 :       return WillRelativeChangeZIndex(aSelection, -1, aCancel, aHandled);
     680             :     case EditAction::increaseZIndex:
     681           0 :       return WillRelativeChangeZIndex(aSelection, 1, aCancel, aHandled);
     682             :     default:
     683           0 :       return TextEditRules::WillDoAction(aSelection, aInfo,
     684           0 :                                          aCancel, aHandled);
     685             :   }
     686             : }
     687             : 
     688             : NS_IMETHODIMP
     689           0 : HTMLEditRules::DidDoAction(Selection* aSelection,
     690             :                            RulesInfo* aInfo,
     691             :                            nsresult aResult)
     692             : {
     693           0 :   TextRulesInfo* info = static_cast<TextRulesInfo*>(aInfo);
     694           0 :   switch (info->action) {
     695             :     case EditAction::insertBreak:
     696           0 :       return DidInsertBreak(aSelection, aResult);
     697             :     case EditAction::deleteSelection:
     698           0 :       return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
     699             :     case EditAction::makeBasicBlock:
     700             :     case EditAction::indent:
     701             :     case EditAction::outdent:
     702             :     case EditAction::align:
     703           0 :       return DidMakeBasicBlock(aSelection, aInfo, aResult);
     704             :     case EditAction::setAbsolutePosition: {
     705           0 :       nsresult rv = DidMakeBasicBlock(aSelection, aInfo, aResult);
     706           0 :       NS_ENSURE_SUCCESS(rv, rv);
     707           0 :       return DidAbsolutePosition();
     708             :     }
     709             :     default:
     710             :       // pass through to TextEditRules
     711           0 :       return TextEditRules::DidDoAction(aSelection, aInfo, aResult);
     712             :   }
     713             : }
     714             : 
     715             : NS_IMETHODIMP_(bool)
     716           0 : HTMLEditRules::DocumentIsEmpty()
     717             : {
     718           0 :   return !!mBogusNode;
     719             : }
     720             : 
     721             : nsresult
     722           0 : HTMLEditRules::GetListState(bool* aMixed,
     723             :                             bool* aOL,
     724             :                             bool* aUL,
     725             :                             bool* aDL)
     726             : {
     727           0 :   NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER);
     728           0 :   *aMixed = false;
     729           0 :   *aOL = false;
     730           0 :   *aUL = false;
     731           0 :   *aDL = false;
     732           0 :   bool bNonList = false;
     733             : 
     734           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
     735             :   nsresult rv = GetListActionNodes(arrayOfNodes, EntireList::no,
     736           0 :                                    TouchContent::no);
     737           0 :   NS_ENSURE_SUCCESS(rv, rv);
     738             : 
     739             :   // Examine list type for nodes in selection.
     740           0 :   for (const auto& curNode : arrayOfNodes) {
     741           0 :     if (!curNode->IsElement()) {
     742           0 :       bNonList = true;
     743           0 :     } else if (curNode->IsHTMLElement(nsGkAtoms::ul)) {
     744           0 :       *aUL = true;
     745           0 :     } else if (curNode->IsHTMLElement(nsGkAtoms::ol)) {
     746           0 :       *aOL = true;
     747           0 :     } else if (curNode->IsHTMLElement(nsGkAtoms::li)) {
     748           0 :       if (dom::Element* parent = curNode->GetParentElement()) {
     749           0 :         if (parent->IsHTMLElement(nsGkAtoms::ul)) {
     750           0 :           *aUL = true;
     751           0 :         } else if (parent->IsHTMLElement(nsGkAtoms::ol)) {
     752           0 :           *aOL = true;
     753             :         }
     754             :       }
     755           0 :     } else if (curNode->IsAnyOfHTMLElements(nsGkAtoms::dl,
     756             :                                             nsGkAtoms::dt,
     757             :                                             nsGkAtoms::dd)) {
     758           0 :       *aDL = true;
     759             :     } else {
     760           0 :       bNonList = true;
     761             :     }
     762             :   }
     763             : 
     764             :   // hokey arithmetic with booleans
     765           0 :   if ((*aUL + *aOL + *aDL + bNonList) > 1) {
     766           0 :     *aMixed = true;
     767             :   }
     768             : 
     769           0 :   return NS_OK;
     770             : }
     771             : 
     772             : nsresult
     773           0 : HTMLEditRules::GetListItemState(bool* aMixed,
     774             :                                 bool* aLI,
     775             :                                 bool* aDT,
     776             :                                 bool* aDD)
     777             : {
     778           0 :   NS_ENSURE_TRUE(aMixed && aLI && aDT && aDD, NS_ERROR_NULL_POINTER);
     779           0 :   *aMixed = false;
     780           0 :   *aLI = false;
     781           0 :   *aDT = false;
     782           0 :   *aDD = false;
     783           0 :   bool bNonList = false;
     784             : 
     785           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
     786             :   nsresult rv = GetListActionNodes(arrayOfNodes, EntireList::no,
     787           0 :                                    TouchContent::no);
     788           0 :   NS_ENSURE_SUCCESS(rv, rv);
     789             : 
     790             :   // examine list type for nodes in selection
     791           0 :   for (const auto& node : arrayOfNodes) {
     792           0 :     if (!node->IsElement()) {
     793           0 :       bNonList = true;
     794           0 :     } else if (node->IsAnyOfHTMLElements(nsGkAtoms::ul,
     795             :                                          nsGkAtoms::ol,
     796             :                                          nsGkAtoms::li)) {
     797           0 :       *aLI = true;
     798           0 :     } else if (node->IsHTMLElement(nsGkAtoms::dt)) {
     799           0 :       *aDT = true;
     800           0 :     } else if (node->IsHTMLElement(nsGkAtoms::dd)) {
     801           0 :       *aDD = true;
     802           0 :     } else if (node->IsHTMLElement(nsGkAtoms::dl)) {
     803             :       // need to look inside dl and see which types of items it has
     804             :       bool bDT, bDD;
     805           0 :       GetDefinitionListItemTypes(node->AsElement(), &bDT, &bDD);
     806           0 :       *aDT |= bDT;
     807           0 :       *aDD |= bDD;
     808             :     } else {
     809           0 :       bNonList = true;
     810             :     }
     811             :   }
     812             : 
     813             :   // hokey arithmetic with booleans
     814           0 :   if (*aDT + *aDD + bNonList > 1) {
     815           0 :     *aMixed = true;
     816             :   }
     817             : 
     818           0 :   return NS_OK;
     819             : }
     820             : 
     821             : nsresult
     822           0 : HTMLEditRules::GetAlignment(bool* aMixed,
     823             :                             nsIHTMLEditor::EAlignment* aAlign)
     824             : {
     825           0 :   MOZ_ASSERT(aMixed && aAlign);
     826             : 
     827           0 :   NS_ENSURE_STATE(mHTMLEditor);
     828           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
     829             : 
     830             :   // For now, just return first alignment.  We'll lie about if it's mixed.
     831             :   // This is for efficiency given that our current ui doesn't care if it's
     832             :   // mixed.
     833             :   // cmanske: NOT TRUE! We would like to pay attention to mixed state in Format
     834             :   // | Align submenu!
     835             : 
     836             :   // This routine assumes that alignment is done ONLY via divs
     837             : 
     838             :   // Default alignment is left
     839           0 :   *aMixed = false;
     840           0 :   *aAlign = nsIHTMLEditor::eLeft;
     841             : 
     842             :   // Get selection
     843           0 :   NS_ENSURE_STATE(htmlEditor->GetSelection());
     844           0 :   OwningNonNull<Selection> selection = *htmlEditor->GetSelection();
     845             : 
     846             :   // Get selection location
     847           0 :   NS_ENSURE_TRUE(htmlEditor->GetRoot(), NS_ERROR_FAILURE);
     848           0 :   OwningNonNull<Element> root = *htmlEditor->GetRoot();
     849             : 
     850           0 :   int32_t rootOffset = root->GetParentNode() ?
     851           0 :                        root->GetParentNode()->IndexOf(root) : -1;
     852             : 
     853           0 :   NS_ENSURE_STATE(selection->GetRangeAt(0) &&
     854             :                   selection->GetRangeAt(0)->GetStartContainer());
     855             :   OwningNonNull<nsINode> parent =
     856           0 :     *selection->GetRangeAt(0)->GetStartContainer();
     857           0 :   int32_t offset = selection->GetRangeAt(0)->StartOffset();
     858             : 
     859             :   // Is the selection collapsed?
     860           0 :   nsCOMPtr<nsINode> nodeToExamine;
     861           0 :   if (selection->Collapsed() || parent->GetAsText()) {
     862             :     // If selection is collapsed, we want to look at 'parent' and its ancestors
     863             :     // for divs with alignment on them.  If we are in a text node, then that is
     864             :     // the node of interest.
     865           0 :     nodeToExamine = parent;
     866           0 :   } else if (parent->IsHTMLElement(nsGkAtoms::html) && offset == rootOffset) {
     867             :     // If we have selected the body, let's look at the first editable node
     868           0 :     nodeToExamine = htmlEditor->GetNextNode(parent, offset, true);
     869             :   } else {
     870           0 :     nsTArray<RefPtr<nsRange>> arrayOfRanges;
     871           0 :     GetPromotedRanges(selection, arrayOfRanges, EditAction::align);
     872             : 
     873             :     // Use these ranges to construct a list of nodes to act on.
     874           0 :     nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
     875             :     nsresult rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
     876           0 :                                        EditAction::align, TouchContent::no);
     877           0 :     NS_ENSURE_SUCCESS(rv, rv);
     878           0 :     nodeToExamine = arrayOfNodes.SafeElementAt(0);
     879             :   }
     880             : 
     881           0 :   NS_ENSURE_TRUE(nodeToExamine, NS_ERROR_NULL_POINTER);
     882             : 
     883           0 :   nsCOMPtr<Element> blockParent = htmlEditor->GetBlock(*nodeToExamine);
     884             : 
     885           0 :   NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE);
     886             : 
     887           0 :   if (htmlEditor->IsCSSEnabled() &&
     888           0 :       htmlEditor->mCSSEditUtils->IsCSSEditableProperty(blockParent, nullptr,
     889           0 :                                                        nsGkAtoms::align)) {
     890             :     // We are in CSS mode and we know how to align this element with CSS
     891           0 :     nsAutoString value;
     892             :     // Let's get the value(s) of text-align or margin-left/margin-right
     893           0 :     htmlEditor->mCSSEditUtils->GetCSSEquivalentToHTMLInlineStyleSet(
     894           0 :         blockParent, nullptr, nsGkAtoms::align, value, CSSEditUtils::eComputed);
     895           0 :     if (value.EqualsLiteral("center") ||
     896           0 :         value.EqualsLiteral("-moz-center") ||
     897           0 :         value.EqualsLiteral("auto auto")) {
     898           0 :       *aAlign = nsIHTMLEditor::eCenter;
     899           0 :       return NS_OK;
     900             :     }
     901           0 :     if (value.EqualsLiteral("right") ||
     902           0 :         value.EqualsLiteral("-moz-right") ||
     903           0 :         value.EqualsLiteral("auto 0px")) {
     904           0 :       *aAlign = nsIHTMLEditor::eRight;
     905           0 :       return NS_OK;
     906             :     }
     907           0 :     if (value.EqualsLiteral("justify")) {
     908           0 :       *aAlign = nsIHTMLEditor::eJustify;
     909           0 :       return NS_OK;
     910             :     }
     911           0 :     *aAlign = nsIHTMLEditor::eLeft;
     912           0 :     return NS_OK;
     913             :   }
     914             : 
     915             :   // Check up the ladder for divs with alignment
     916           0 :   bool isFirstNodeToExamine = true;
     917           0 :   for (; nodeToExamine; nodeToExamine = nodeToExamine->GetParentNode()) {
     918           0 :     if (!isFirstNodeToExamine &&
     919           0 :         nodeToExamine->IsHTMLElement(nsGkAtoms::table)) {
     920             :       // The node to examine is a table and this is not the first node we
     921             :       // examine; let's break here to materialize the 'inline-block' behaviour
     922             :       // of html tables regarding to text alignment
     923           0 :       return NS_OK;
     924             :     }
     925           0 :     if (HTMLEditUtils::SupportsAlignAttr(*nodeToExamine)) {
     926             :       // Check for alignment
     927           0 :       nsAutoString typeAttrVal;
     928           0 :       nodeToExamine->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::align,
     929           0 :                                           typeAttrVal);
     930           0 :       ToLowerCase(typeAttrVal);
     931           0 :       if (!typeAttrVal.IsEmpty()) {
     932           0 :         if (typeAttrVal.EqualsLiteral("center")) {
     933           0 :           *aAlign = nsIHTMLEditor::eCenter;
     934           0 :         } else if (typeAttrVal.EqualsLiteral("right")) {
     935           0 :           *aAlign = nsIHTMLEditor::eRight;
     936           0 :         } else if (typeAttrVal.EqualsLiteral("justify")) {
     937           0 :           *aAlign = nsIHTMLEditor::eJustify;
     938             :         } else {
     939           0 :           *aAlign = nsIHTMLEditor::eLeft;
     940             :         }
     941           0 :         return NS_OK;
     942             :       }
     943             :     }
     944           0 :     isFirstNodeToExamine = false;
     945             :   }
     946           0 :   return NS_OK;
     947             : }
     948             : 
     949           0 : static nsIAtom& MarginPropertyAtomForIndent(CSSEditUtils& aHTMLCSSUtils,
     950             :                                             nsINode& aNode)
     951             : {
     952           0 :   nsAutoString direction;
     953           0 :   aHTMLCSSUtils.GetComputedProperty(aNode, *nsGkAtoms::direction, direction);
     954           0 :   return direction.EqualsLiteral("rtl") ?
     955           0 :     *nsGkAtoms::marginRight : *nsGkAtoms::marginLeft;
     956             : }
     957             : 
     958             : nsresult
     959           0 : HTMLEditRules::GetIndentState(bool* aCanIndent,
     960             :                               bool* aCanOutdent)
     961             : {
     962             :   // XXX Looks like that this is implementation of
     963             :   //     nsIHTMLEditor::getIndentState() however nobody calls this method
     964             :   //     even with the interface method.
     965           0 :   NS_ENSURE_TRUE(aCanIndent && aCanOutdent, NS_ERROR_FAILURE);
     966           0 :   *aCanIndent = true;
     967           0 :   *aCanOutdent = false;
     968             : 
     969             :   // get selection
     970           0 :   NS_ENSURE_STATE(mHTMLEditor && mHTMLEditor->GetSelection());
     971           0 :   OwningNonNull<Selection> selection = *mHTMLEditor->GetSelection();
     972             : 
     973             :   // contruct a list of nodes to act on.
     974           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
     975           0 :   nsresult rv = GetNodesFromSelection(*selection, EditAction::indent,
     976           0 :                                       arrayOfNodes, TouchContent::no);
     977           0 :   NS_ENSURE_SUCCESS(rv, rv);
     978             : 
     979             :   // examine nodes in selection for blockquotes or list elements;
     980             :   // these we can outdent.  Note that we return true for canOutdent
     981             :   // if *any* of the selection is outdentable, rather than all of it.
     982           0 :   NS_ENSURE_STATE(mHTMLEditor);
     983           0 :   bool useCSS = mHTMLEditor->IsCSSEnabled();
     984           0 :   for (auto& curNode : Reversed(arrayOfNodes)) {
     985           0 :     if (HTMLEditUtils::IsNodeThatCanOutdent(GetAsDOMNode(curNode))) {
     986           0 :       *aCanOutdent = true;
     987           0 :       break;
     988           0 :     } else if (useCSS) {
     989             :       // we are in CSS mode, indentation is done using the margin-left (or margin-right) property
     990           0 :       NS_ENSURE_STATE(mHTMLEditor);
     991             :       nsIAtom& marginProperty =
     992           0 :         MarginPropertyAtomForIndent(*mHTMLEditor->mCSSEditUtils, curNode);
     993           0 :       nsAutoString value;
     994             :       // retrieve its specified value
     995           0 :       NS_ENSURE_STATE(mHTMLEditor);
     996           0 :       mHTMLEditor->mCSSEditUtils->GetSpecifiedProperty(*curNode,
     997           0 :                                                        marginProperty, value);
     998             :       float f;
     999           0 :       nsCOMPtr<nsIAtom> unit;
    1000             :       // get its number part and its unit
    1001           0 :       NS_ENSURE_STATE(mHTMLEditor);
    1002           0 :       mHTMLEditor->mCSSEditUtils->ParseLength(value, &f, getter_AddRefs(unit));
    1003             :       // if the number part is strictly positive, outdent is possible
    1004           0 :       if (0 < f) {
    1005           0 :         *aCanOutdent = true;
    1006           0 :         break;
    1007             :       }
    1008             :     }
    1009             :   }
    1010             : 
    1011           0 :   if (!*aCanOutdent) {
    1012             :     // if we haven't found something to outdent yet, also check the parents
    1013             :     // of selection endpoints.  We might have a blockquote or list item
    1014             :     // in the parent hierarchy.
    1015             : 
    1016             :     // gather up info we need for test
    1017           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1018           0 :     nsCOMPtr<nsIDOMNode> parent, tmp, root = do_QueryInterface(mHTMLEditor->GetRoot());
    1019           0 :     NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
    1020             :     int32_t selOffset;
    1021           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1022           0 :     RefPtr<Selection> selection = mHTMLEditor->GetSelection();
    1023           0 :     NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
    1024             : 
    1025             :     // test start parent hierarchy
    1026           0 :     rv = EditorBase::GetStartNodeAndOffset(selection, getter_AddRefs(parent),
    1027           0 :                                            &selOffset);
    1028           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1029           0 :     while (parent && parent != root) {
    1030           0 :       if (HTMLEditUtils::IsNodeThatCanOutdent(parent)) {
    1031           0 :         *aCanOutdent = true;
    1032           0 :         break;
    1033             :       }
    1034           0 :       tmp = parent;
    1035           0 :       tmp->GetParentNode(getter_AddRefs(parent));
    1036             :     }
    1037             : 
    1038             :     // test end parent hierarchy
    1039           0 :     rv = EditorBase::GetEndNodeAndOffset(selection, getter_AddRefs(parent),
    1040           0 :                                          &selOffset);
    1041           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1042           0 :     while (parent && parent != root) {
    1043           0 :       if (HTMLEditUtils::IsNodeThatCanOutdent(parent)) {
    1044           0 :         *aCanOutdent = true;
    1045           0 :         break;
    1046             :       }
    1047           0 :       tmp = parent;
    1048           0 :       tmp->GetParentNode(getter_AddRefs(parent));
    1049             :     }
    1050             :   }
    1051           0 :   return NS_OK;
    1052             : }
    1053             : 
    1054             : 
    1055             : nsresult
    1056           0 : HTMLEditRules::GetParagraphState(bool* aMixed,
    1057             :                                  nsAString& outFormat)
    1058             : {
    1059             :   // This routine is *heavily* tied to our ui choices in the paragraph
    1060             :   // style popup.  I can't see a way around that.
    1061           0 :   NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
    1062           0 :   *aMixed = true;
    1063           0 :   outFormat.Truncate(0);
    1064             : 
    1065           0 :   bool bMixed = false;
    1066             :   // using "x" as an uninitialized value, since "" is meaningful
    1067           0 :   nsAutoString formatStr(NS_LITERAL_STRING("x"));
    1068             : 
    1069           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
    1070           0 :   nsresult rv = GetParagraphFormatNodes(arrayOfNodes, TouchContent::no);
    1071           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1072             : 
    1073             :   // post process list.  We need to replace any block nodes that are not format
    1074             :   // nodes with their content.  This is so we only have to look "up" the hierarchy
    1075             :   // to find format nodes, instead of both up and down.
    1076           0 :   for (int32_t i = arrayOfNodes.Length() - 1; i >= 0; i--) {
    1077           0 :     auto& curNode = arrayOfNodes[i];
    1078           0 :     nsAutoString format;
    1079             :     // if it is a known format node we have it easy
    1080           0 :     if (IsBlockNode(curNode) && !HTMLEditUtils::IsFormatNode(curNode)) {
    1081             :       // arrayOfNodes.RemoveObject(curNode);
    1082           0 :       rv = AppendInnerFormatNodes(arrayOfNodes, curNode);
    1083           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1084             :     }
    1085             :   }
    1086             : 
    1087             :   // we might have an empty node list.  if so, find selection parent
    1088             :   // and put that on the list
    1089           0 :   if (arrayOfNodes.IsEmpty()) {
    1090           0 :     nsCOMPtr<nsINode> selNode;
    1091             :     int32_t selOffset;
    1092           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1093           0 :     RefPtr<Selection> selection = mHTMLEditor->GetSelection();
    1094           0 :     NS_ENSURE_STATE(selection);
    1095           0 :     rv = EditorBase::GetStartNodeAndOffset(selection, getter_AddRefs(selNode),
    1096           0 :                                            &selOffset);
    1097           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1098           0 :     NS_ENSURE_TRUE(selNode, NS_ERROR_NULL_POINTER);
    1099           0 :     arrayOfNodes.AppendElement(*selNode);
    1100             :   }
    1101             : 
    1102             :   // remember root node
    1103           0 :   NS_ENSURE_STATE(mHTMLEditor);
    1104           0 :   nsCOMPtr<nsIDOMElement> rootElem = do_QueryInterface(mHTMLEditor->GetRoot());
    1105           0 :   NS_ENSURE_TRUE(rootElem, NS_ERROR_NULL_POINTER);
    1106             : 
    1107             :   // loop through the nodes in selection and examine their paragraph format
    1108           0 :   for (auto& curNode : Reversed(arrayOfNodes)) {
    1109           0 :     nsAutoString format;
    1110             :     // if it is a known format node we have it easy
    1111           0 :     if (HTMLEditUtils::IsFormatNode(curNode)) {
    1112           0 :       GetFormatString(GetAsDOMNode(curNode), format);
    1113           0 :     } else if (IsBlockNode(curNode)) {
    1114             :       // this is a div or some other non-format block.
    1115             :       // we should ignore it.  Its children were appended to this list
    1116             :       // by AppendInnerFormatNodes() call above.  We will get needed
    1117             :       // info when we examine them instead.
    1118           0 :       continue;
    1119             :     } else {
    1120           0 :       nsCOMPtr<nsIDOMNode> node, tmp = GetAsDOMNode(curNode);
    1121           0 :       tmp->GetParentNode(getter_AddRefs(node));
    1122           0 :       while (node) {
    1123           0 :         if (node == rootElem) {
    1124           0 :           format.Truncate(0);
    1125           0 :           break;
    1126           0 :         } else if (HTMLEditUtils::IsFormatNode(node)) {
    1127           0 :           GetFormatString(node, format);
    1128           0 :           break;
    1129             :         }
    1130             :         // else keep looking up
    1131           0 :         tmp = node;
    1132           0 :         tmp->GetParentNode(getter_AddRefs(node));
    1133             :       }
    1134             :     }
    1135             : 
    1136             :     // if this is the first node, we've found, remember it as the format
    1137           0 :     if (formatStr.EqualsLiteral("x")) {
    1138           0 :       formatStr = format;
    1139             :     }
    1140             :     // else make sure it matches previously found format
    1141           0 :     else if (format != formatStr) {
    1142           0 :       bMixed = true;
    1143           0 :       break;
    1144             :     }
    1145             :   }
    1146             : 
    1147           0 :   *aMixed = bMixed;
    1148           0 :   outFormat = formatStr;
    1149           0 :   return NS_OK;
    1150             : }
    1151             : 
    1152             : nsresult
    1153           0 : HTMLEditRules::AppendInnerFormatNodes(nsTArray<OwningNonNull<nsINode>>& aArray,
    1154             :                                       nsINode* aNode)
    1155             : {
    1156           0 :   MOZ_ASSERT(aNode);
    1157             : 
    1158             :   // we only need to place any one inline inside this node onto
    1159             :   // the list.  They are all the same for purposes of determining
    1160             :   // paragraph style.  We use foundInline to track this as we are
    1161             :   // going through the children in the loop below.
    1162           0 :   bool foundInline = false;
    1163           0 :   for (nsIContent* child = aNode->GetFirstChild();
    1164           0 :        child;
    1165           0 :        child = child->GetNextSibling()) {
    1166           0 :     bool isBlock = IsBlockNode(*child);
    1167           0 :     bool isFormat = HTMLEditUtils::IsFormatNode(child);
    1168           0 :     if (isBlock && !isFormat) {
    1169             :       // if it's a div, etc., recurse
    1170           0 :       AppendInnerFormatNodes(aArray, child);
    1171           0 :     } else if (isFormat) {
    1172           0 :       aArray.AppendElement(*child);
    1173           0 :     } else if (!foundInline) {
    1174             :       // if this is the first inline we've found, use it
    1175           0 :       foundInline = true;
    1176           0 :       aArray.AppendElement(*child);
    1177             :     }
    1178             :   }
    1179           0 :   return NS_OK;
    1180             : }
    1181             : 
    1182             : nsresult
    1183           0 : HTMLEditRules::GetFormatString(nsIDOMNode* aNode,
    1184             :                                nsAString& outFormat)
    1185             : {
    1186           0 :   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
    1187             : 
    1188           0 :   if (HTMLEditUtils::IsFormatNode(aNode)) {
    1189           0 :     nsCOMPtr<nsIAtom> atom = EditorBase::GetTag(aNode);
    1190           0 :     atom->ToString(outFormat);
    1191             :   } else {
    1192           0 :     outFormat.Truncate();
    1193             :   }
    1194           0 :   return NS_OK;
    1195             : }
    1196             : 
    1197             : void
    1198           0 : HTMLEditRules::WillInsert(Selection& aSelection,
    1199             :                           bool* aCancel)
    1200             : {
    1201           0 :   MOZ_ASSERT(aCancel);
    1202             : 
    1203           0 :   TextEditRules::WillInsert(aSelection, aCancel);
    1204             : 
    1205           0 :   NS_ENSURE_TRUE_VOID(mHTMLEditor);
    1206           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    1207             : 
    1208             :   // Adjust selection to prevent insertion after a moz-BR.  This next only
    1209             :   // works for collapsed selections right now, because selection is a pain to
    1210             :   // work with when not collapsed.  (no good way to extend start or end of
    1211             :   // selection), so we ignore those types of selections.
    1212           0 :   if (!aSelection.Collapsed()) {
    1213           0 :     return;
    1214             :   }
    1215             : 
    1216             :   // If we are after a mozBR in the same block, then move selection to be
    1217             :   // before it
    1218           0 :   NS_ENSURE_TRUE_VOID(aSelection.GetRangeAt(0) &&
    1219             :                       aSelection.GetRangeAt(0)->GetStartContainer());
    1220             :   OwningNonNull<nsINode> selNode =
    1221           0 :     *aSelection.GetRangeAt(0)->GetStartContainer();
    1222           0 :   int32_t selOffset = aSelection.GetRangeAt(0)->StartOffset();
    1223             : 
    1224             :   // Get prior node
    1225             :   nsCOMPtr<nsIContent> priorNode = htmlEditor->GetPriorHTMLNode(selNode,
    1226           0 :                                                                 selOffset);
    1227           0 :   if (priorNode && TextEditUtils::IsMozBR(priorNode)) {
    1228           0 :     nsCOMPtr<Element> block1 = htmlEditor->GetBlock(selNode);
    1229           0 :     nsCOMPtr<Element> block2 = htmlEditor->GetBlockNodeParent(priorNode);
    1230             : 
    1231           0 :     if (block1 && block1 == block2) {
    1232             :       // If we are here then the selection is right after a mozBR that is in
    1233             :       // the same block as the selection.  We need to move the selection start
    1234             :       // to be before the mozBR.
    1235           0 :       selNode = priorNode->GetParentNode();
    1236           0 :       selOffset = selNode->IndexOf(priorNode);
    1237           0 :       nsresult rv = aSelection.Collapse(selNode, selOffset);
    1238           0 :       NS_ENSURE_SUCCESS_VOID(rv);
    1239             :     }
    1240             :   }
    1241             : 
    1242           0 :   if (mDidDeleteSelection &&
    1243           0 :       (mTheAction == EditAction::insertText ||
    1244           0 :        mTheAction == EditAction::insertIMEText ||
    1245           0 :        mTheAction == EditAction::deleteSelection)) {
    1246           0 :     nsresult rv = ReapplyCachedStyles();
    1247           0 :     NS_ENSURE_SUCCESS_VOID(rv);
    1248             :   }
    1249             :   // For most actions we want to clear the cached styles, but there are
    1250             :   // exceptions
    1251           0 :   if (!IsStyleCachePreservingAction(mTheAction)) {
    1252           0 :     ClearCachedStyles();
    1253             :   }
    1254             : }
    1255             : 
    1256             : nsresult
    1257           0 : HTMLEditRules::WillInsertText(EditAction aAction,
    1258             :                               Selection* aSelection,
    1259             :                               bool* aCancel,
    1260             :                               bool* aHandled,
    1261             :                               const nsAString* inString,
    1262             :                               nsAString* outString,
    1263             :                               int32_t aMaxLength)
    1264             : {
    1265           0 :   if (!aSelection || !aCancel || !aHandled) {
    1266           0 :     return NS_ERROR_NULL_POINTER;
    1267             :   }
    1268             : 
    1269             :   // initialize out param
    1270           0 :   *aCancel = false;
    1271           0 :   *aHandled = true;
    1272             :   // If the selection isn't collapsed, delete it.  Don't delete existing inline
    1273             :   // tags, because we're hopefully going to insert text (bug 787432).
    1274           0 :   if (!aSelection->Collapsed()) {
    1275           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1276             :     nsresult rv =
    1277           0 :       mHTMLEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eNoStrip);
    1278           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1279             :   }
    1280             : 
    1281           0 :   WillInsert(*aSelection, aCancel);
    1282             :   // initialize out param
    1283             :   // we want to ignore result of WillInsert()
    1284           0 :   *aCancel = false;
    1285             : 
    1286             :   // we need to get the doc
    1287           0 :   NS_ENSURE_STATE(mHTMLEditor);
    1288           0 :   nsCOMPtr<nsIDocument> doc = mHTMLEditor->GetDocument();
    1289           0 :   NS_ENSURE_STATE(doc);
    1290             : 
    1291             :   // for every property that is set, insert a new inline style node
    1292           0 :   nsresult rv = CreateStyleForInsertText(*aSelection, *doc);
    1293           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1294             : 
    1295             :   // get the (collapsed) selection location
    1296           0 :   NS_ENSURE_STATE(mHTMLEditor);
    1297           0 :   NS_ENSURE_STATE(aSelection->GetRangeAt(0));
    1298           0 :   nsCOMPtr<nsINode> selNode = aSelection->GetRangeAt(0)->GetStartContainer();
    1299           0 :   int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset();
    1300           0 :   NS_ENSURE_STATE(selNode);
    1301             : 
    1302             :   // dont put text in places that can't have it
    1303           0 :   NS_ENSURE_STATE(mHTMLEditor);
    1304           0 :   if (!EditorBase::IsTextNode(selNode) &&
    1305           0 :       (!mHTMLEditor || !mHTMLEditor->CanContainTag(*selNode,
    1306             :                                                    *nsGkAtoms::textTagName))) {
    1307           0 :     return NS_ERROR_FAILURE;
    1308             :   }
    1309             : 
    1310           0 :   if (aAction == EditAction::insertIMEText) {
    1311             :     // Right now the WSRunObject code bails on empty strings, but IME needs
    1312             :     // the InsertTextImpl() call to still happen since empty strings are meaningful there.
    1313           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1314             :     // If there is one or more IME selections, its minimum offset should be
    1315             :     // the insertion point.
    1316             :     int32_t IMESelectionOffset =
    1317           0 :       mHTMLEditor->GetIMESelectionStartOffsetIn(selNode);
    1318           0 :     if (IMESelectionOffset >= 0) {
    1319           0 :       selOffset = IMESelectionOffset;
    1320             :     }
    1321           0 :     if (inString->IsEmpty()) {
    1322           0 :       rv = mHTMLEditor->InsertTextImpl(*inString, address_of(selNode),
    1323           0 :                                        &selOffset, doc);
    1324           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1325           0 :         return rv;
    1326             :       }
    1327             :     } else {
    1328           0 :       WSRunObject wsObj(mHTMLEditor, selNode, selOffset);
    1329           0 :       rv = wsObj.InsertText(*inString, address_of(selNode), &selOffset, doc);
    1330           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1331           0 :         return rv;
    1332             :       }
    1333             :     }
    1334             :   }
    1335             :   // aAction == kInsertText
    1336             :   else {
    1337             :     // find where we are
    1338           0 :     nsCOMPtr<nsINode> curNode = selNode;
    1339           0 :     int32_t curOffset = selOffset;
    1340             : 
    1341             :     // is our text going to be PREformatted?
    1342             :     // We remember this so that we know how to handle tabs.
    1343             :     bool isPRE;
    1344           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1345           0 :     rv = mHTMLEditor->IsPreformatted(GetAsDOMNode(selNode), &isPRE);
    1346           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1347             : 
    1348             :     // turn off the edit listener: we know how to
    1349             :     // build the "doc changed range" ourselves, and it's
    1350             :     // must faster to do it once here than to track all
    1351             :     // the changes one at a time.
    1352           0 :     AutoLockListener lockit(&mListenerEnabled);
    1353             : 
    1354             :     // don't spaz my selection in subtransactions
    1355           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1356           0 :     AutoTransactionsConserveSelection dontSpazMySelection(mHTMLEditor);
    1357           0 :     nsAutoString tString(*inString);
    1358           0 :     const char16_t *unicodeBuf = tString.get();
    1359           0 :     int32_t pos = 0;
    1360           0 :     NS_NAMED_LITERAL_STRING(newlineStr, LFSTR);
    1361             : 
    1362             :     {
    1363           0 :       NS_ENSURE_STATE(mHTMLEditor);
    1364           0 :       AutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
    1365           0 :                                 address_of(selNode), &selOffset);
    1366             : 
    1367             :       // for efficiency, break out the pre case separately.  This is because
    1368             :       // its a lot cheaper to search the input string for only newlines than
    1369             :       // it is to search for both tabs and newlines.
    1370           0 :       if (isPRE || IsPlaintextEditor()) {
    1371           0 :         while (unicodeBuf && pos != -1 &&
    1372           0 :                pos < static_cast<int32_t>(inString->Length())) {
    1373           0 :           int32_t oldPos = pos;
    1374             :           int32_t subStrLen;
    1375           0 :           pos = tString.FindChar(nsCRT::LF, oldPos);
    1376             : 
    1377           0 :           if (pos != -1) {
    1378           0 :             subStrLen = pos - oldPos;
    1379             :             // if first char is newline, then use just it
    1380           0 :             if (!subStrLen) {
    1381           0 :               subStrLen = 1;
    1382             :             }
    1383             :           } else {
    1384           0 :             subStrLen = tString.Length() - oldPos;
    1385           0 :             pos = tString.Length();
    1386             :           }
    1387             : 
    1388           0 :           nsDependentSubstring subStr(tString, oldPos, subStrLen);
    1389             : 
    1390             :           // is it a return?
    1391           0 :           if (subStr.Equals(newlineStr)) {
    1392           0 :             NS_ENSURE_STATE(mHTMLEditor);
    1393             :             nsCOMPtr<Element> br =
    1394           0 :               mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset,
    1395           0 :                                         nsIEditor::eNone);
    1396           0 :             NS_ENSURE_STATE(br);
    1397           0 :             pos++;
    1398             :           } else {
    1399           0 :             NS_ENSURE_STATE(mHTMLEditor);
    1400           0 :             rv = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode),
    1401           0 :                                              &curOffset, doc);
    1402           0 :             NS_ENSURE_SUCCESS(rv, rv);
    1403             :           }
    1404             :         }
    1405             :       } else {
    1406           0 :         NS_NAMED_LITERAL_STRING(tabStr, "\t");
    1407           0 :         NS_NAMED_LITERAL_STRING(spacesStr, "    ");
    1408           0 :         char specialChars[] = {TAB, nsCRT::LF, 0};
    1409           0 :         while (unicodeBuf && pos != -1 &&
    1410           0 :                pos < static_cast<int32_t>(inString->Length())) {
    1411           0 :           int32_t oldPos = pos;
    1412             :           int32_t subStrLen;
    1413           0 :           pos = tString.FindCharInSet(specialChars, oldPos);
    1414             : 
    1415           0 :           if (pos != -1) {
    1416           0 :             subStrLen = pos - oldPos;
    1417             :             // if first char is newline, then use just it
    1418           0 :             if (!subStrLen) {
    1419           0 :               subStrLen = 1;
    1420             :             }
    1421             :           } else {
    1422           0 :             subStrLen = tString.Length() - oldPos;
    1423           0 :             pos = tString.Length();
    1424             :           }
    1425             : 
    1426           0 :           nsDependentSubstring subStr(tString, oldPos, subStrLen);
    1427           0 :           NS_ENSURE_STATE(mHTMLEditor);
    1428           0 :           WSRunObject wsObj(mHTMLEditor, curNode, curOffset);
    1429             : 
    1430             :           // is it a tab?
    1431           0 :           if (subStr.Equals(tabStr)) {
    1432             :             rv =
    1433           0 :               wsObj.InsertText(spacesStr, address_of(curNode), &curOffset, doc);
    1434           0 :             NS_ENSURE_SUCCESS(rv, rv);
    1435           0 :             pos++;
    1436             :           }
    1437             :           // is it a return?
    1438           0 :           else if (subStr.Equals(newlineStr)) {
    1439           0 :             nsCOMPtr<Element> br = wsObj.InsertBreak(address_of(curNode),
    1440             :                                                      &curOffset,
    1441           0 :                                                      nsIEditor::eNone);
    1442           0 :             NS_ENSURE_TRUE(br, NS_ERROR_FAILURE);
    1443           0 :             pos++;
    1444             :           } else {
    1445           0 :             rv = wsObj.InsertText(subStr, address_of(curNode), &curOffset, doc);
    1446           0 :             NS_ENSURE_SUCCESS(rv, rv);
    1447             :           }
    1448             :         }
    1449             :       }
    1450             :     }
    1451           0 :     aSelection->SetInterlinePosition(false);
    1452           0 :     if (curNode) aSelection->Collapse(curNode, curOffset);
    1453             :     // manually update the doc changed range so that AfterEdit will clean up
    1454             :     // the correct portion of the document.
    1455           0 :     if (!mDocChangeRange) {
    1456           0 :       mDocChangeRange = new nsRange(selNode);
    1457             :     }
    1458             : 
    1459           0 :     if (curNode) {
    1460           0 :       rv = mDocChangeRange->SetStartAndEnd(selNode, selOffset,
    1461           0 :                                            curNode, curOffset);
    1462           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1463           0 :         return rv;
    1464             :       }
    1465             :     } else {
    1466           0 :       rv = mDocChangeRange->CollapseTo(selNode, selOffset);
    1467           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1468           0 :         return rv;
    1469             :       }
    1470             :     }
    1471             :   }
    1472           0 :   return NS_OK;
    1473             : }
    1474             : 
    1475             : nsresult
    1476           0 : HTMLEditRules::WillLoadHTML(Selection* aSelection,
    1477             :                             bool* aCancel)
    1478             : {
    1479           0 :   NS_ENSURE_TRUE(aSelection && aCancel, NS_ERROR_NULL_POINTER);
    1480             : 
    1481           0 :   *aCancel = false;
    1482             : 
    1483             :   // Delete mBogusNode if it exists. If we really need one,
    1484             :   // it will be added during post-processing in AfterEditInner().
    1485             : 
    1486           0 :   if (mBogusNode) {
    1487           0 :     if (NS_WARN_IF(!mHTMLEditor)) {
    1488           0 :       return NS_ERROR_UNEXPECTED;
    1489             :     }
    1490           0 :     mHTMLEditor->DeleteNode(mBogusNode);
    1491           0 :     mBogusNode = nullptr;
    1492             :   }
    1493             : 
    1494           0 :   return NS_OK;
    1495             : }
    1496             : 
    1497             : nsresult
    1498           0 : HTMLEditRules::WillInsertBreak(Selection& aSelection,
    1499             :                                bool* aCancel,
    1500             :                                bool* aHandled)
    1501             : {
    1502           0 :   MOZ_ASSERT(aCancel && aHandled);
    1503           0 :   *aCancel = false;
    1504           0 :   *aHandled = false;
    1505             : 
    1506           0 :   NS_ENSURE_STATE(mHTMLEditor);
    1507           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    1508             : 
    1509             :   // If the selection isn't collapsed, delete it.
    1510           0 :   if (!aSelection.Collapsed()) {
    1511             :     nsresult rv =
    1512           0 :       htmlEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
    1513           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1514             :   }
    1515             : 
    1516           0 :   WillInsert(aSelection, aCancel);
    1517             : 
    1518             :   // Initialize out param.  We want to ignore result of WillInsert().
    1519           0 :   *aCancel = false;
    1520             : 
    1521             :   // Split any mailcites in the way.  Should we abort this if we encounter
    1522             :   // table cell boundaries?
    1523           0 :   if (IsMailEditor()) {
    1524           0 :     nsresult rv = SplitMailCites(&aSelection, aHandled);
    1525           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1526           0 :     if (*aHandled) {
    1527           0 :       return NS_OK;
    1528             :     }
    1529             :   }
    1530             : 
    1531             :   // Smart splitting rules
    1532           0 :   NS_ENSURE_TRUE(aSelection.GetRangeAt(0) &&
    1533             :                  aSelection.GetRangeAt(0)->GetStartContainer(),
    1534             :                  NS_ERROR_FAILURE);
    1535           0 :   OwningNonNull<nsINode> node = *aSelection.GetRangeAt(0)->GetStartContainer();
    1536           0 :   int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
    1537             : 
    1538             :   // Do nothing if the node is read-only
    1539           0 :   if (!htmlEditor->IsModifiableNode(node)) {
    1540           0 :     *aCancel = true;
    1541           0 :     return NS_OK;
    1542             :   }
    1543             : 
    1544             :   // Identify the block
    1545           0 :   nsCOMPtr<Element> blockParent = htmlEditor->GetBlock(node);
    1546           0 :   NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE);
    1547             : 
    1548             :   // If the active editing host is an inline element, or if the active editing
    1549             :   // host is the block parent itself and we're configured to use <br> as a
    1550             :   // paragraph separator, just append a <br>.
    1551           0 :   nsCOMPtr<Element> host = htmlEditor->GetActiveEditingHost();
    1552           0 :   if (NS_WARN_IF(!host)) {
    1553           0 :     return NS_ERROR_FAILURE;
    1554             :   }
    1555           0 :   ParagraphSeparator separator = mHTMLEditor->GetDefaultParagraphSeparator();
    1556           0 :   if (!IsBlockNode(*host) ||
    1557             :       // The nodes that can contain p and div are the same.  If the editing
    1558             :       // host is a <p> or similar, we have to just insert a newline.
    1559           0 :       (!mHTMLEditor->CanContainTag(*host, *nsGkAtoms::p) &&
    1560             :        // These can't contain <p> as a child, but can as a descendant, so we
    1561             :        // don't have to fall back to inserting a newline.
    1562           0 :        !host->IsAnyOfHTMLElements(nsGkAtoms::ol, nsGkAtoms::ul, nsGkAtoms::dl,
    1563             :                                   nsGkAtoms::table, nsGkAtoms::thead,
    1564             :                                   nsGkAtoms::tbody, nsGkAtoms::tfoot,
    1565           0 :                                   nsGkAtoms::tr)) ||
    1566           0 :       (host == blockParent && separator == ParagraphSeparator::br)) {
    1567           0 :     nsresult rv = StandardBreakImpl(node, offset, aSelection);
    1568           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1569           0 :     *aHandled = true;
    1570           0 :     return NS_OK;
    1571             :   }
    1572           0 :   if (host == blockParent && separator != ParagraphSeparator::br) {
    1573             :     // Insert a new block first
    1574           0 :     MOZ_ASSERT(separator == ParagraphSeparator::div ||
    1575             :                separator == ParagraphSeparator::p);
    1576           0 :     nsresult rv = MakeBasicBlock(aSelection,
    1577           0 :                                  ParagraphSeparatorElement(separator));
    1578             :     // We warn on failure, but don't handle it, because it might be harmless.
    1579             :     // Instead we just check that a new block was actually created.
    1580           0 :     Unused << NS_WARN_IF(NS_FAILED(rv));
    1581             : 
    1582             :     // Reinitialize node/offset in case they're not inside the new block
    1583           0 :     if (NS_WARN_IF(!aSelection.GetRangeAt(0) ||
    1584             :                    !aSelection.GetRangeAt(0)->GetStartContainer())) {
    1585           0 :       return NS_ERROR_FAILURE;
    1586             :     }
    1587           0 :     node = *aSelection.GetRangeAt(0)->GetStartContainer();
    1588           0 :     offset = aSelection.GetRangeAt(0)->StartOffset();
    1589             : 
    1590           0 :     blockParent = mHTMLEditor->GetBlock(node);
    1591           0 :     if (NS_WARN_IF(!blockParent)) {
    1592           0 :       return NS_ERROR_UNEXPECTED;
    1593             :     }
    1594           0 :     if (NS_WARN_IF(blockParent == host)) {
    1595             :       // Didn't create a new block for some reason, fall back to <br>
    1596           0 :       rv = StandardBreakImpl(node, offset, aSelection);
    1597           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1598           0 :         return rv;
    1599             :       }
    1600           0 :       *aHandled = true;
    1601           0 :       return NS_OK;
    1602             :     }
    1603             :   }
    1604             : 
    1605             :   // If block is empty, populate with br.  (For example, imagine a div that
    1606             :   // contains the word "text".  The user selects "text" and types return.
    1607             :   // "Text" is deleted leaving an empty block.  We want to put in one br to
    1608             :   // make block have a line.  Then code further below will put in a second br.)
    1609             :   bool isEmpty;
    1610           0 :   IsEmptyBlock(*blockParent, &isEmpty);
    1611           0 :   if (isEmpty) {
    1612           0 :     nsCOMPtr<Element> br = htmlEditor->CreateBR(blockParent,
    1613           0 :                                                 blockParent->Length());
    1614           0 :     NS_ENSURE_STATE(br);
    1615             :   }
    1616             : 
    1617           0 :   nsCOMPtr<Element> listItem = IsInListItem(blockParent);
    1618           0 :   if (listItem && listItem != host) {
    1619           0 :     ReturnInListItem(aSelection, *listItem, node, offset);
    1620           0 :     *aHandled = true;
    1621           0 :     return NS_OK;
    1622           0 :   } else if (HTMLEditUtils::IsHeader(*blockParent)) {
    1623             :     // Headers: close (or split) header
    1624           0 :     ReturnInHeader(aSelection, *blockParent, node, offset);
    1625           0 :     *aHandled = true;
    1626           0 :     return NS_OK;
    1627           0 :   } else if (blockParent->IsAnyOfHTMLElements(nsGkAtoms::p, nsGkAtoms::div)) {
    1628             :     // Paragraphs: special rules to look for <br>s
    1629             :     nsresult rv =
    1630           0 :       ReturnInParagraph(&aSelection, GetAsDOMNode(blockParent),
    1631           0 :                         GetAsDOMNode(node), offset, aCancel, aHandled);
    1632           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1633             :     // Fall through, we may not have handled it in ReturnInParagraph()
    1634             :   }
    1635             : 
    1636             :   // If not already handled then do the standard thing
    1637           0 :   if (!(*aHandled)) {
    1638           0 :     *aHandled = true;
    1639           0 :     return StandardBreakImpl(node, offset, aSelection);
    1640             :   }
    1641           0 :   return NS_OK;
    1642             : }
    1643             : 
    1644             : nsresult
    1645           0 : HTMLEditRules::StandardBreakImpl(nsINode& aNode,
    1646             :                                  int32_t aOffset,
    1647             :                                  Selection& aSelection)
    1648             : {
    1649           0 :   NS_ENSURE_STATE(mHTMLEditor);
    1650           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    1651             : 
    1652           0 :   nsCOMPtr<Element> brNode;
    1653           0 :   bool bAfterBlock = false;
    1654           0 :   bool bBeforeBlock = false;
    1655           0 :   nsCOMPtr<nsINode> node = &aNode;
    1656             : 
    1657           0 :   if (IsPlaintextEditor()) {
    1658           0 :     brNode = htmlEditor->CreateBR(node, aOffset);
    1659           0 :     NS_ENSURE_STATE(brNode);
    1660             :   } else {
    1661           0 :     WSRunObject wsObj(htmlEditor, node, aOffset);
    1662           0 :     int32_t visOffset = 0;
    1663           0 :     WSType wsType;
    1664           0 :     nsCOMPtr<nsINode> visNode;
    1665           0 :     wsObj.PriorVisibleNode(node, aOffset, address_of(visNode),
    1666           0 :                            &visOffset, &wsType);
    1667           0 :     if (wsType & WSType::block) {
    1668           0 :       bAfterBlock = true;
    1669             :     }
    1670           0 :     wsObj.NextVisibleNode(node, aOffset, address_of(visNode),
    1671           0 :                           &visOffset, &wsType);
    1672           0 :     if (wsType & WSType::block) {
    1673           0 :       bBeforeBlock = true;
    1674             :     }
    1675           0 :     nsCOMPtr<nsIDOMNode> linkDOMNode;
    1676           0 :     if (htmlEditor->IsInLink(GetAsDOMNode(node), address_of(linkDOMNode))) {
    1677             :       // Split the link
    1678           0 :       nsCOMPtr<Element> linkNode = do_QueryInterface(linkDOMNode);
    1679           0 :       NS_ENSURE_STATE(linkNode || !linkDOMNode);
    1680           0 :       nsCOMPtr<nsINode> linkParent = linkNode->GetParentNode();
    1681           0 :       aOffset = htmlEditor->SplitNodeDeep(*linkNode, *node->AsContent(),
    1682             :                                           aOffset,
    1683             :                                           HTMLEditor::EmptyContainers::no);
    1684           0 :       NS_ENSURE_STATE(aOffset != -1);
    1685           0 :       node = linkParent;
    1686             :     }
    1687           0 :     brNode = wsObj.InsertBreak(address_of(node), &aOffset, nsIEditor::eNone);
    1688           0 :     NS_ENSURE_TRUE(brNode, NS_ERROR_FAILURE);
    1689             :   }
    1690           0 :   node = brNode->GetParentNode();
    1691           0 :   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
    1692           0 :   int32_t offset = node->IndexOf(brNode);
    1693           0 :   if (bAfterBlock && bBeforeBlock) {
    1694             :     // We just placed a br between block boundaries.  This is the one case
    1695             :     // where we want the selection to be before the br we just placed, as the
    1696             :     // br will be on a new line, rather than at end of prior line.
    1697           0 :     aSelection.SetInterlinePosition(true);
    1698           0 :     nsresult rv = aSelection.Collapse(node, offset);
    1699           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1700             :   } else {
    1701           0 :     WSRunObject wsObj(htmlEditor, node, offset + 1);
    1702           0 :     nsCOMPtr<nsINode> secondBR;
    1703           0 :     int32_t visOffset = 0;
    1704           0 :     WSType wsType;
    1705           0 :     wsObj.NextVisibleNode(node, offset + 1, address_of(secondBR),
    1706           0 :                           &visOffset, &wsType);
    1707           0 :     if (wsType == WSType::br) {
    1708             :       // The next thing after the break we inserted is another break.  Move the
    1709             :       // second break to be the first break's sibling.  This will prevent them
    1710             :       // from being in different inline nodes, which would break
    1711             :       // SetInterlinePosition().  It will also assure that if the user clicks
    1712             :       // away and then clicks back on their new blank line, they will still get
    1713             :       // the style from the line above.
    1714           0 :       nsCOMPtr<nsINode> brParent = secondBR->GetParentNode();
    1715           0 :       int32_t brOffset = brParent ? brParent->IndexOf(secondBR) : -1;
    1716           0 :       if (brParent != node || brOffset != offset + 1) {
    1717             :         nsresult rv =
    1718           0 :           htmlEditor->MoveNode(secondBR->AsContent(), node, offset + 1);
    1719           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1720             :       }
    1721             :     }
    1722             :     // SetInterlinePosition(true) means we want the caret to stick to the
    1723             :     // content on the "right".  We want the caret to stick to whatever is past
    1724             :     // the break.  This is because the break is on the same line we were on,
    1725             :     // but the next content will be on the following line.
    1726             : 
    1727             :     // An exception to this is if the break has a next sibling that is a block
    1728             :     // node.  Then we stick to the left to avoid an uber caret.
    1729           0 :     nsCOMPtr<nsIContent> siblingNode = brNode->GetNextSibling();
    1730           0 :     if (siblingNode && IsBlockNode(*siblingNode)) {
    1731           0 :       aSelection.SetInterlinePosition(false);
    1732             :     } else {
    1733           0 :       aSelection.SetInterlinePosition(true);
    1734             :     }
    1735           0 :     nsresult rv = aSelection.Collapse(node, offset + 1);
    1736           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1737             :   }
    1738           0 :   return NS_OK;
    1739             : }
    1740             : 
    1741             : nsresult
    1742           0 : HTMLEditRules::DidInsertBreak(Selection* aSelection,
    1743             :                               nsresult aResult)
    1744             : {
    1745           0 :   return NS_OK;
    1746             : }
    1747             : 
    1748             : nsresult
    1749           0 : HTMLEditRules::SplitMailCites(Selection* aSelection,
    1750             :                               bool* aHandled)
    1751             : {
    1752           0 :   NS_ENSURE_TRUE(aSelection && aHandled, NS_ERROR_NULL_POINTER);
    1753           0 :   nsCOMPtr<nsIContent> leftCite, rightCite;
    1754           0 :   nsCOMPtr<nsINode> selNode;
    1755           0 :   nsCOMPtr<Element> citeNode;
    1756             :   int32_t selOffset;
    1757             :   nsresult rv =
    1758           0 :     EditorBase::GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode),
    1759           0 :                                       &selOffset);
    1760           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1761           0 :   citeNode = GetTopEnclosingMailCite(*selNode);
    1762           0 :   if (citeNode) {
    1763             :     // If our selection is just before a break, nudge it to be
    1764             :     // just after it.  This does two things for us.  It saves us the trouble of having to add
    1765             :     // a break here ourselves to preserve the "blockness" of the inline span mailquote
    1766             :     // (in the inline case), and :
    1767             :     // it means the break won't end up making an empty line that happens to be inside a
    1768             :     // mailquote (in either inline or block case).
    1769             :     // The latter can confuse a user if they click there and start typing,
    1770             :     // because being in the mailquote may affect wrapping behavior, or font color, etc.
    1771           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1772           0 :     WSRunObject wsObj(mHTMLEditor, selNode, selOffset);
    1773           0 :     nsCOMPtr<nsINode> visNode;
    1774           0 :     int32_t visOffset=0;
    1775           0 :     WSType wsType;
    1776           0 :     wsObj.NextVisibleNode(selNode, selOffset, address_of(visNode),
    1777           0 :                           &visOffset, &wsType);
    1778           0 :     if (wsType == WSType::br) {
    1779             :       // ok, we are just before a break.  is it inside the mailquote?
    1780           0 :       if (visNode != citeNode && citeNode->Contains(visNode)) {
    1781             :         // it is.  so lets reset our selection to be just after it.
    1782           0 :         NS_ENSURE_STATE(mHTMLEditor);
    1783           0 :         selNode = mHTMLEditor->GetNodeLocation(visNode, &selOffset);
    1784           0 :         ++selOffset;
    1785             :       }
    1786             :     }
    1787             : 
    1788           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1789           0 :     NS_ENSURE_STATE(selNode->IsContent());
    1790           0 :     int32_t newOffset = mHTMLEditor->SplitNodeDeep(*citeNode,
    1791           0 :         *selNode->AsContent(), selOffset, HTMLEditor::EmptyContainers::no,
    1792           0 :         getter_AddRefs(leftCite), getter_AddRefs(rightCite));
    1793           0 :     NS_ENSURE_STATE(newOffset != -1);
    1794             : 
    1795             :     // Add an invisible <br> to the end of the left part if it was a <span> of
    1796             :     // style="display: block". This is important, since when serialising the
    1797             :     // cite to plain text, the span which caused the visual break is discarded.
    1798             :     // So the added <br> will guarantee that the serialiser will insert a
    1799             :     // break where the user saw one.
    1800           0 :     if (leftCite &&
    1801           0 :         leftCite->IsHTMLElement(nsGkAtoms::span) &&
    1802           0 :         leftCite->GetPrimaryFrame()->IsFrameOfType(nsIFrame::eBlockFrame)) {
    1803           0 :        nsCOMPtr<nsINode> lastChild = leftCite->GetLastChild();
    1804           0 :        if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
    1805             :          // We ignore the result here.
    1806             :          nsCOMPtr<Element> invisBR =
    1807           0 :            mHTMLEditor->CreateBR(leftCite, leftCite->Length());
    1808             :       }
    1809             :     }
    1810             : 
    1811           0 :     selNode = citeNode->GetParentNode();
    1812           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1813           0 :     nsCOMPtr<Element> brNode = mHTMLEditor->CreateBR(selNode, newOffset);
    1814           0 :     NS_ENSURE_STATE(brNode);
    1815             : 
    1816             :     // want selection before the break, and on same line
    1817           0 :     aSelection->SetInterlinePosition(true);
    1818           0 :     rv = aSelection->Collapse(selNode, newOffset);
    1819           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1820             : 
    1821             :     // if citeNode wasn't a block, we might also want another break before it.
    1822             :     // We need to examine the content both before the br we just added and also
    1823             :     // just after it.  If we don't have another br or block boundary adjacent,
    1824             :     // then we will need a 2nd br added to achieve blank line that user expects.
    1825           0 :     if (IsInlineNode(*citeNode)) {
    1826           0 :       NS_ENSURE_STATE(mHTMLEditor);
    1827           0 :       WSRunObject wsObj(mHTMLEditor, selNode, newOffset);
    1828           0 :       nsCOMPtr<nsINode> visNode;
    1829           0 :       int32_t visOffset=0;
    1830           0 :       WSType wsType;
    1831           0 :       wsObj.PriorVisibleNode(selNode, newOffset, address_of(visNode),
    1832           0 :                              &visOffset, &wsType);
    1833           0 :       if (wsType == WSType::normalWS || wsType == WSType::text ||
    1834           0 :           wsType == WSType::special) {
    1835           0 :         NS_ENSURE_STATE(mHTMLEditor);
    1836           0 :         WSRunObject wsObjAfterBR(mHTMLEditor, selNode, newOffset+1);
    1837           0 :         wsObjAfterBR.NextVisibleNode(selNode, newOffset + 1,
    1838           0 :                                      address_of(visNode), &visOffset, &wsType);
    1839           0 :         if (wsType == WSType::normalWS || wsType == WSType::text ||
    1840           0 :             wsType == WSType::special ||
    1841             :             // In case we're at the very end.
    1842           0 :             wsType == WSType::thisBlock) {
    1843           0 :           NS_ENSURE_STATE(mHTMLEditor);
    1844           0 :           brNode = mHTMLEditor->CreateBR(selNode, newOffset);
    1845           0 :           NS_ENSURE_STATE(brNode);
    1846             :         }
    1847             :       }
    1848             :     }
    1849             : 
    1850             :     // delete any empty cites
    1851           0 :     bool bEmptyCite = false;
    1852           0 :     if (leftCite) {
    1853           0 :       NS_ENSURE_STATE(mHTMLEditor);
    1854           0 :       rv = mHTMLEditor->IsEmptyNode(leftCite, &bEmptyCite, true, false);
    1855           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1856           0 :         return rv;
    1857             :       }
    1858           0 :       if (bEmptyCite) {
    1859           0 :         NS_ENSURE_STATE(mHTMLEditor);
    1860           0 :         rv = mHTMLEditor->DeleteNode(leftCite);
    1861           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    1862           0 :           return rv;
    1863             :         }
    1864             :       }
    1865             :     }
    1866             : 
    1867           0 :     if (rightCite) {
    1868           0 :       NS_ENSURE_STATE(mHTMLEditor);
    1869           0 :       rv = mHTMLEditor->IsEmptyNode(rightCite, &bEmptyCite, true, false);
    1870           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1871           0 :         return rv;
    1872             :       }
    1873           0 :       if (bEmptyCite) {
    1874           0 :         NS_ENSURE_STATE(mHTMLEditor);
    1875           0 :         rv = mHTMLEditor->DeleteNode(rightCite);
    1876           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    1877           0 :           return rv;
    1878             :         }
    1879             :       }
    1880             :     }
    1881           0 :     *aHandled = true;
    1882             :   }
    1883           0 :   return NS_OK;
    1884             : }
    1885             : 
    1886             : 
    1887             : nsresult
    1888           0 : HTMLEditRules::WillDeleteSelection(Selection* aSelection,
    1889             :                                    nsIEditor::EDirection aAction,
    1890             :                                    nsIEditor::EStripWrappers aStripWrappers,
    1891             :                                    bool* aCancel,
    1892             :                                    bool* aHandled)
    1893             : {
    1894           0 :   MOZ_ASSERT(aStripWrappers == nsIEditor::eStrip ||
    1895             :              aStripWrappers == nsIEditor::eNoStrip);
    1896             : 
    1897           0 :   if (!aSelection || !aCancel || !aHandled) {
    1898           0 :     return NS_ERROR_NULL_POINTER;
    1899             :   }
    1900             :   // Initialize out params
    1901           0 :   *aCancel = false;
    1902           0 :   *aHandled = false;
    1903             : 
    1904             :   // Remember that we did a selection deletion.  Used by CreateStyleForInsertText()
    1905           0 :   mDidDeleteSelection = true;
    1906             : 
    1907             :   // If there is only bogus content, cancel the operation
    1908           0 :   if (mBogusNode) {
    1909           0 :     *aCancel = true;
    1910           0 :     return NS_OK;
    1911             :   }
    1912             : 
    1913             :   // First check for table selection mode.  If so, hand off to table editor.
    1914           0 :   nsCOMPtr<nsIDOMElement> cell;
    1915           0 :   NS_ENSURE_STATE(mHTMLEditor);
    1916             :   nsresult rv =
    1917           0 :     mHTMLEditor->GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
    1918           0 :   if (NS_SUCCEEDED(rv) && cell) {
    1919           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1920           0 :     rv = mHTMLEditor->DeleteTableCellContents();
    1921           0 :     *aHandled = true;
    1922           0 :     return rv;
    1923             :   }
    1924           0 :   cell = nullptr;
    1925             : 
    1926             :   // origCollapsed is used later to determine whether we should join blocks. We
    1927             :   // don't really care about bCollapsed because it will be modified by
    1928             :   // ExtendSelectionForDelete later. TryToJoinBlocks() should happen if the
    1929             :   // original selection is collapsed and the cursor is at the end of a block
    1930             :   // element, in which case ExtendSelectionForDelete would always make the
    1931             :   // selection not collapsed.
    1932           0 :   bool bCollapsed = aSelection->Collapsed();
    1933           0 :   bool join = false;
    1934           0 :   bool origCollapsed = bCollapsed;
    1935             : 
    1936           0 :   nsCOMPtr<nsINode> selNode;
    1937             :   int32_t selOffset;
    1938             : 
    1939           0 :   NS_ENSURE_STATE(aSelection->GetRangeAt(0));
    1940           0 :   nsCOMPtr<nsINode> startNode = aSelection->GetRangeAt(0)->GetStartContainer();
    1941           0 :   int32_t startOffset = aSelection->GetRangeAt(0)->StartOffset();
    1942           0 :   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
    1943             : 
    1944           0 :   if (bCollapsed) {
    1945             :     // If we are inside an empty block, delete it.
    1946           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1947           0 :     nsCOMPtr<Element> host = mHTMLEditor->GetActiveEditingHost();
    1948           0 :     NS_ENSURE_TRUE(host, NS_ERROR_FAILURE);
    1949           0 :     rv = CheckForEmptyBlock(startNode, host, aSelection, aAction, aHandled);
    1950           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1951           0 :     if (*aHandled) {
    1952           0 :       return NS_OK;
    1953             :     }
    1954             : 
    1955             :     // Test for distance between caret and text that will be deleted
    1956           0 :     rv = CheckBidiLevelForDeletion(aSelection, GetAsDOMNode(startNode),
    1957           0 :                                    startOffset, aAction, aCancel);
    1958           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1959           0 :     if (*aCancel) {
    1960           0 :       return NS_OK;
    1961             :     }
    1962             : 
    1963           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1964           0 :     rv = mHTMLEditor->ExtendSelectionForDelete(aSelection, &aAction);
    1965           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1966             : 
    1967             :     // We should delete nothing.
    1968           0 :     if (aAction == nsIEditor::eNone) {
    1969           0 :       return NS_OK;
    1970             :     }
    1971             : 
    1972             :     // ExtendSelectionForDelete() may have changed the selection, update it
    1973           0 :     NS_ENSURE_STATE(aSelection->GetRangeAt(0));
    1974           0 :     startNode = aSelection->GetRangeAt(0)->GetStartContainer();
    1975           0 :     startOffset = aSelection->GetRangeAt(0)->StartOffset();
    1976           0 :     NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
    1977             : 
    1978           0 :     bCollapsed = aSelection->Collapsed();
    1979             :   }
    1980             : 
    1981           0 :   if (bCollapsed) {
    1982             :     // What's in the direction we are deleting?
    1983           0 :     NS_ENSURE_STATE(mHTMLEditor);
    1984           0 :     WSRunObject wsObj(mHTMLEditor, startNode, startOffset);
    1985           0 :     nsCOMPtr<nsINode> visNode;
    1986             :     int32_t visOffset;
    1987           0 :     WSType wsType;
    1988             : 
    1989             :     // Find next visible node
    1990           0 :     if (aAction == nsIEditor::eNext) {
    1991           0 :       wsObj.NextVisibleNode(startNode, startOffset, address_of(visNode),
    1992           0 :                             &visOffset, &wsType);
    1993             :     } else {
    1994           0 :       wsObj.PriorVisibleNode(startNode, startOffset, address_of(visNode),
    1995           0 :                              &visOffset, &wsType);
    1996             :     }
    1997             : 
    1998           0 :     if (!visNode) {
    1999             :       // Can't find anything to delete!
    2000           0 :       *aCancel = true;
    2001             :       // XXX This is the result of mHTMLEditor->GetFirstSelectedCell().
    2002             :       //     The value could be both an error and NS_OK.
    2003           0 :       return rv;
    2004             :     }
    2005             : 
    2006           0 :     if (wsType == WSType::normalWS) {
    2007             :       // We found some visible ws to delete.  Let ws code handle it.
    2008           0 :       *aHandled = true;
    2009           0 :       if (aAction == nsIEditor::eNext) {
    2010           0 :         rv = wsObj.DeleteWSForward();
    2011           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    2012           0 :           return rv;
    2013             :         }
    2014             :       } else {
    2015           0 :         rv = wsObj.DeleteWSBackward();
    2016           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    2017           0 :           return rv;
    2018             :         }
    2019             :       }
    2020           0 :       return InsertBRIfNeeded(aSelection);
    2021             :     }
    2022             : 
    2023           0 :     if (wsType == WSType::text) {
    2024             :       // Found normal text to delete.
    2025           0 :       OwningNonNull<Text> nodeAsText = *visNode->GetAsText();
    2026           0 :       int32_t so = visOffset;
    2027           0 :       int32_t eo = visOffset + 1;
    2028           0 :       if (aAction == nsIEditor::ePrevious) {
    2029           0 :         if (!so) {
    2030           0 :           return NS_ERROR_UNEXPECTED;
    2031             :         }
    2032           0 :         so--;
    2033           0 :         eo--;
    2034             :         // Bug 1068979: delete both codepoints if surrogate pair
    2035           0 :         if (so > 0) {
    2036           0 :           const nsTextFragment *text = nodeAsText->GetText();
    2037           0 :           if (NS_IS_LOW_SURROGATE(text->CharAt(so)) &&
    2038           0 :               NS_IS_HIGH_SURROGATE(text->CharAt(so - 1))) {
    2039           0 :             so--;
    2040             :           }
    2041             :         }
    2042             :       } else {
    2043           0 :         RefPtr<nsRange> range = aSelection->GetRangeAt(0);
    2044           0 :         NS_ENSURE_STATE(range);
    2045             : 
    2046           0 :         NS_ASSERTION(range->GetStartContainer() == visNode,
    2047             :                      "selection start not in visNode");
    2048           0 :         NS_ASSERTION(range->GetEndContainer() == visNode,
    2049             :                      "selection end not in visNode");
    2050             : 
    2051           0 :         so = range->StartOffset();
    2052           0 :         eo = range->EndOffset();
    2053             :       }
    2054           0 :       NS_ENSURE_STATE(mHTMLEditor);
    2055           0 :       rv = WSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(visNode),
    2056           0 :                                              &so, address_of(visNode), &eo);
    2057           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2058           0 :       NS_ENSURE_STATE(mHTMLEditor);
    2059           0 :       *aHandled = true;
    2060           0 :       rv = mHTMLEditor->DeleteText(nodeAsText, std::min(so, eo),
    2061           0 :                                    DeprecatedAbs(eo - so));
    2062           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2063             : 
    2064             :       // XXX When Backspace key is pressed, Chromium removes following empty
    2065             :       //     text nodes when removing the last character of the non-empty text
    2066             :       //     node.  However, Edge never removes empty text nodes even if
    2067             :       //     selection is in the following empty text node(s).  For now, we
    2068             :       //     should keep our traditional behavior same as Edge for backward
    2069             :       //     compatibility.
    2070             :       // XXX When Delete key is pressed, Edge removes all preceding empty
    2071             :       //     text nodes when removing the first character of the non-empty
    2072             :       //     text node.  Chromium removes only selected empty text node and
    2073             :       //     following empty text nodes and the first character of the
    2074             :       //     non-empty text node.  For now, we should keep our traditional
    2075             :       //     behavior same as Chromium for backward compatibility.
    2076             : 
    2077           0 :       DeleteNodeIfCollapsedText(nodeAsText);
    2078             : 
    2079           0 :       rv = InsertBRIfNeeded(aSelection);
    2080           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2081             : 
    2082             :       // Remember that we did a ranged delete for the benefit of
    2083             :       // AfterEditInner().
    2084           0 :       mDidRangedDelete = true;
    2085             : 
    2086           0 :       return NS_OK;
    2087             :     }
    2088             : 
    2089           0 :     if (wsType == WSType::special || wsType == WSType::br ||
    2090           0 :         visNode->IsHTMLElement(nsGkAtoms::hr)) {
    2091             :       // Short circuit for invisible breaks.  delete them and recurse.
    2092           0 :       if (visNode->IsHTMLElement(nsGkAtoms::br) &&
    2093           0 :           (!mHTMLEditor || !mHTMLEditor->IsVisBreak(visNode))) {
    2094           0 :         NS_ENSURE_STATE(mHTMLEditor);
    2095           0 :         rv = mHTMLEditor->DeleteNode(visNode);
    2096           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2097           0 :         return WillDeleteSelection(aSelection, aAction, aStripWrappers,
    2098           0 :                                    aCancel, aHandled);
    2099             :       }
    2100             : 
    2101             :       // Special handling for backspace when positioned after <hr>
    2102           0 :       if (aAction == nsIEditor::ePrevious &&
    2103           0 :           visNode->IsHTMLElement(nsGkAtoms::hr)) {
    2104             :         // Only if the caret is positioned at the end-of-hr-line position, we
    2105             :         // want to delete the <hr>.
    2106             :         //
    2107             :         // In other words, we only want to delete, if our selection position
    2108             :         // (indicated by startNode and startOffset) is the position directly
    2109             :         // after the <hr>, on the same line as the <hr>.
    2110             :         //
    2111             :         // To detect this case we check:
    2112             :         // startNode == parentOfVisNode
    2113             :         // and
    2114             :         // startOffset -1 == visNodeOffsetToVisNodeParent
    2115             :         // and
    2116             :         // interline position is false (left)
    2117             :         //
    2118             :         // In any other case we set the position to startnode -1 and
    2119             :         // interlineposition to false, only moving the caret to the
    2120             :         // end-of-hr-line position.
    2121           0 :         bool moveOnly = true;
    2122             : 
    2123           0 :         selNode = visNode->GetParentNode();
    2124           0 :         selOffset = selNode ? selNode->IndexOf(visNode) : -1;
    2125             : 
    2126             :         bool interLineIsRight;
    2127           0 :         rv = aSelection->GetInterlinePosition(&interLineIsRight);
    2128           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2129             : 
    2130           0 :         if (startNode == selNode && startOffset - 1 == selOffset &&
    2131           0 :             !interLineIsRight) {
    2132           0 :           moveOnly = false;
    2133             :         }
    2134             : 
    2135           0 :         if (moveOnly) {
    2136             :           // Go to the position after the <hr>, but to the end of the <hr> line
    2137             :           // by setting the interline position to left.
    2138           0 :           ++selOffset;
    2139           0 :           aSelection->Collapse(selNode, selOffset);
    2140           0 :           aSelection->SetInterlinePosition(false);
    2141           0 :           mDidExplicitlySetInterline = true;
    2142           0 :           *aHandled = true;
    2143             : 
    2144             :           // There is one exception to the move only case.  If the <hr> is
    2145             :           // followed by a <br> we want to delete the <br>.
    2146             : 
    2147           0 :           WSType otherWSType;
    2148           0 :           nsCOMPtr<nsINode> otherNode;
    2149             :           int32_t otherOffset;
    2150             : 
    2151           0 :           wsObj.NextVisibleNode(startNode, startOffset, address_of(otherNode),
    2152           0 :                                 &otherOffset, &otherWSType);
    2153             : 
    2154           0 :           if (otherWSType == WSType::br) {
    2155             :             // Delete the <br>
    2156             : 
    2157           0 :             NS_ENSURE_STATE(mHTMLEditor);
    2158           0 :             nsCOMPtr<nsIContent> otherContent(do_QueryInterface(otherNode));
    2159           0 :             rv = WSRunObject::PrepareToDeleteNode(mHTMLEditor, otherContent);
    2160           0 :             NS_ENSURE_SUCCESS(rv, rv);
    2161           0 :             NS_ENSURE_STATE(mHTMLEditor);
    2162           0 :             rv = mHTMLEditor->DeleteNode(otherNode);
    2163           0 :             NS_ENSURE_SUCCESS(rv, rv);
    2164             :           }
    2165             : 
    2166           0 :           return NS_OK;
    2167             :         }
    2168             :         // Else continue with normal delete code
    2169             :       }
    2170             : 
    2171             :       // Found break or image, or hr.
    2172           0 :       NS_ENSURE_STATE(mHTMLEditor);
    2173           0 :       NS_ENSURE_STATE(visNode->IsContent());
    2174           0 :       rv = WSRunObject::PrepareToDeleteNode(mHTMLEditor, visNode->AsContent());
    2175           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2176             :       // Remember sibling to visnode, if any
    2177           0 :       NS_ENSURE_STATE(mHTMLEditor);
    2178           0 :       nsCOMPtr<nsIContent> sibling = mHTMLEditor->GetPriorHTMLSibling(visNode);
    2179             :       // Delete the node, and join like nodes if appropriate
    2180           0 :       NS_ENSURE_STATE(mHTMLEditor);
    2181           0 :       rv = mHTMLEditor->DeleteNode(visNode);
    2182           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2183             :       // We did something, so let's say so.
    2184           0 :       *aHandled = true;
    2185             :       // Is there a prior node and are they siblings?
    2186           0 :       nsCOMPtr<nsINode> stepbrother;
    2187           0 :       if (sibling) {
    2188           0 :         NS_ENSURE_STATE(mHTMLEditor);
    2189           0 :         stepbrother = mHTMLEditor->GetNextHTMLSibling(sibling);
    2190             :       }
    2191             :       // Are they both text nodes?  If so, join them!
    2192           0 :       if (startNode == stepbrother && startNode->GetAsText() &&
    2193           0 :           sibling->GetAsText()) {
    2194           0 :         EditorDOMPoint pt = JoinNodesSmart(*sibling, *startNode->AsContent());
    2195           0 :         NS_ENSURE_STATE(pt.node);
    2196             :         // Fix up selection
    2197           0 :         rv = aSelection->Collapse(pt.node, pt.offset);
    2198           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2199             :       }
    2200           0 :       rv = InsertBRIfNeeded(aSelection);
    2201           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2202           0 :       return NS_OK;
    2203             :     }
    2204             : 
    2205           0 :     if (wsType == WSType::otherBlock) {
    2206             :       // Make sure it's not a table element.  If so, cancel the operation
    2207             :       // (translation: users cannot backspace or delete across table cells)
    2208           0 :       if (HTMLEditUtils::IsTableElement(visNode)) {
    2209           0 :         *aCancel = true;
    2210           0 :         return NS_OK;
    2211             :       }
    2212             : 
    2213             :       // Next to a block.  See if we are between a block and a br.  If so, we
    2214             :       // really want to delete the br.  Else join content at selection to the
    2215             :       // block.
    2216           0 :       bool bDeletedBR = false;
    2217           0 :       WSType otherWSType;
    2218           0 :       nsCOMPtr<nsINode> otherNode;
    2219             :       int32_t otherOffset;
    2220             : 
    2221             :       // Find node in other direction
    2222           0 :       if (aAction == nsIEditor::eNext) {
    2223           0 :         wsObj.PriorVisibleNode(startNode, startOffset, address_of(otherNode),
    2224           0 :                                &otherOffset, &otherWSType);
    2225             :       } else {
    2226           0 :         wsObj.NextVisibleNode(startNode, startOffset, address_of(otherNode),
    2227           0 :                               &otherOffset, &otherWSType);
    2228             :       }
    2229             : 
    2230             :       // First find the adjacent node in the block
    2231           0 :       nsCOMPtr<nsIContent> leafNode;
    2232           0 :       nsCOMPtr<nsINode> leftNode, rightNode;
    2233           0 :       if (aAction == nsIEditor::ePrevious) {
    2234           0 :         NS_ENSURE_STATE(mHTMLEditor);
    2235           0 :         leafNode = mHTMLEditor->GetLastEditableLeaf(*visNode);
    2236           0 :         leftNode = leafNode;
    2237           0 :         rightNode = startNode;
    2238             :       } else {
    2239           0 :         NS_ENSURE_STATE(mHTMLEditor);
    2240           0 :         leafNode = mHTMLEditor->GetFirstEditableLeaf(*visNode);
    2241           0 :         leftNode = startNode;
    2242           0 :         rightNode = leafNode;
    2243             :       }
    2244             : 
    2245           0 :       if (otherNode->IsHTMLElement(nsGkAtoms::br)) {
    2246           0 :         NS_ENSURE_STATE(mHTMLEditor);
    2247           0 :         rv = mHTMLEditor->DeleteNode(otherNode);
    2248           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2249             :         // XXX Only in this case, setting "handled" to true only when it
    2250             :         //     succeeds?
    2251           0 :         *aHandled = true;
    2252           0 :         bDeletedBR = true;
    2253             :       }
    2254             : 
    2255             :       // Don't cross table boundaries
    2256           0 :       if (leftNode && rightNode &&
    2257           0 :           InDifferentTableElements(leftNode, rightNode)) {
    2258           0 :         return NS_OK;
    2259             :       }
    2260             : 
    2261           0 :       if (bDeletedBR) {
    2262             :         // Put selection at edge of block and we are done.
    2263           0 :         NS_ENSURE_STATE(leafNode);
    2264           0 :         EditorDOMPoint newSel = GetGoodSelPointForNode(*leafNode, aAction);
    2265           0 :         NS_ENSURE_STATE(newSel.node);
    2266           0 :         aSelection->Collapse(newSel.node, newSel.offset);
    2267           0 :         return NS_OK;
    2268             :       }
    2269             : 
    2270             :       // Else we are joining content to block
    2271             : 
    2272           0 :       nsCOMPtr<nsINode> selPointNode = startNode;
    2273           0 :       int32_t selPointOffset = startOffset;
    2274             :       {
    2275           0 :         NS_ENSURE_STATE(mHTMLEditor);
    2276           0 :         AutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
    2277           0 :                                   address_of(selPointNode), &selPointOffset);
    2278           0 :         NS_ENSURE_STATE(leftNode && leftNode->IsContent() &&
    2279             :                         rightNode && rightNode->IsContent());
    2280             :         EditActionResult ret =
    2281           0 :           TryToJoinBlocks(*leftNode->AsContent(), *rightNode->AsContent());
    2282           0 :         *aHandled |= ret.Handled();
    2283           0 :         *aCancel |= ret.Canceled();
    2284           0 :         if (NS_WARN_IF(ret.Failed())) {
    2285           0 :           return ret.Rv();
    2286             :         }
    2287             :       }
    2288             : 
    2289             :       // If TryToJoinBlocks() didn't handle it  and it's not canceled,
    2290             :       // user may want to modify the start leaf node or the last leaf node
    2291             :       // of the block.
    2292           0 :       if (!*aHandled && !*aCancel && leafNode != startNode) {
    2293             :         int32_t offset =
    2294           0 :           aAction == nsIEditor::ePrevious ?
    2295           0 :             static_cast<int32_t>(leafNode->Length()) : 0;
    2296           0 :         aSelection->Collapse(leafNode, offset);
    2297           0 :         return WillDeleteSelection(aSelection, aAction, aStripWrappers,
    2298           0 :                                    aCancel, aHandled);
    2299             :       }
    2300             : 
    2301             :       // Otherwise, we must have deleted the selection as user expected.
    2302           0 :       aSelection->Collapse(selPointNode, selPointOffset);
    2303           0 :       return NS_OK;
    2304             :     }
    2305             : 
    2306           0 :     if (wsType == WSType::thisBlock) {
    2307             :       // At edge of our block.  Look beside it and see if we can join to an
    2308             :       // adjacent block
    2309             : 
    2310             :       // Make sure it's not a table element.  If so, cancel the operation
    2311             :       // (translation: users cannot backspace or delete across table cells)
    2312           0 :       if (HTMLEditUtils::IsTableElement(visNode)) {
    2313           0 :         *aCancel = true;
    2314           0 :         return NS_OK;
    2315             :       }
    2316             : 
    2317             :       // First find the relevant nodes
    2318           0 :       nsCOMPtr<nsINode> leftNode, rightNode;
    2319           0 :       if (aAction == nsIEditor::ePrevious) {
    2320           0 :         NS_ENSURE_STATE(mHTMLEditor);
    2321           0 :         leftNode = mHTMLEditor->GetPriorHTMLNode(visNode);
    2322           0 :         rightNode = startNode;
    2323             :       } else {
    2324           0 :         NS_ENSURE_STATE(mHTMLEditor);
    2325           0 :         rightNode = mHTMLEditor->GetNextHTMLNode(visNode);
    2326           0 :         leftNode = startNode;
    2327             :       }
    2328             : 
    2329             :       // Nothing to join
    2330           0 :       if (!leftNode || !rightNode) {
    2331           0 :         *aCancel = true;
    2332           0 :         return NS_OK;
    2333             :       }
    2334             : 
    2335             :       // Don't cross table boundaries -- cancel it
    2336           0 :       if (InDifferentTableElements(leftNode, rightNode)) {
    2337           0 :         *aCancel = true;
    2338           0 :         return NS_OK;
    2339             :       }
    2340             : 
    2341           0 :       nsCOMPtr<nsINode> selPointNode = startNode;
    2342           0 :       int32_t selPointOffset = startOffset;
    2343             :       {
    2344           0 :         NS_ENSURE_STATE(mHTMLEditor);
    2345           0 :         AutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
    2346           0 :                                   address_of(selPointNode), &selPointOffset);
    2347           0 :         NS_ENSURE_STATE(leftNode->IsContent() && rightNode->IsContent());
    2348             :         EditActionResult ret =
    2349           0 :           TryToJoinBlocks(*leftNode->AsContent(), *rightNode->AsContent());
    2350             :         // This should claim that trying to join the block means that
    2351             :         // this handles the action because the caller shouldn't do anything
    2352             :         // anymore in this case.
    2353           0 :         *aHandled = true;
    2354           0 :         *aCancel |= ret.Canceled();
    2355           0 :         if (NS_WARN_IF(ret.Failed())) {
    2356           0 :           return ret.Rv();
    2357             :         }
    2358             :       }
    2359           0 :       aSelection->Collapse(selPointNode, selPointOffset);
    2360           0 :       return NS_OK;
    2361             :     }
    2362             :   }
    2363             : 
    2364             : 
    2365             :   // Else we have a non-collapsed selection.  First adjust the selection.
    2366           0 :   rv = ExpandSelectionForDeletion(*aSelection);
    2367           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2368             : 
    2369             :   // Remember that we did a ranged delete for the benefit of AfterEditInner().
    2370           0 :   mDidRangedDelete = true;
    2371             : 
    2372             :   // Refresh start and end points
    2373           0 :   NS_ENSURE_STATE(aSelection->GetRangeAt(0));
    2374           0 :   startNode = aSelection->GetRangeAt(0)->GetStartContainer();
    2375           0 :   startOffset = aSelection->GetRangeAt(0)->StartOffset();
    2376           0 :   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
    2377           0 :   nsCOMPtr<nsINode> endNode = aSelection->GetRangeAt(0)->GetEndContainer();
    2378           0 :   int32_t endOffset = aSelection->GetRangeAt(0)->EndOffset();
    2379           0 :   NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
    2380             : 
    2381             :   // Figure out if the endpoints are in nodes that can be merged.  Adjust
    2382             :   // surrounding whitespace in preparation to delete selection.
    2383           0 :   if (!IsPlaintextEditor()) {
    2384           0 :     NS_ENSURE_STATE(mHTMLEditor);
    2385           0 :     AutoTransactionsConserveSelection dontSpazMySelection(mHTMLEditor);
    2386           0 :     rv = WSRunObject::PrepareToDeleteRange(mHTMLEditor,
    2387             :                                            address_of(startNode), &startOffset,
    2388           0 :                                            address_of(endNode), &endOffset);
    2389           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2390             :   }
    2391             : 
    2392             :   {
    2393             :     // Track location of where we are deleting
    2394           0 :     NS_ENSURE_STATE(mHTMLEditor);
    2395           0 :     AutoTrackDOMPoint startTracker(mHTMLEditor->mRangeUpdater,
    2396           0 :                                    address_of(startNode), &startOffset);
    2397           0 :     AutoTrackDOMPoint endTracker(mHTMLEditor->mRangeUpdater,
    2398           0 :                                  address_of(endNode), &endOffset);
    2399             :     // We are handling all ranged deletions directly now.
    2400           0 :     *aHandled = true;
    2401             : 
    2402           0 :     if (endNode == startNode) {
    2403           0 :       NS_ENSURE_STATE(mHTMLEditor);
    2404           0 :       rv = mHTMLEditor->DeleteSelectionImpl(aAction, aStripWrappers);
    2405           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2406             :     } else {
    2407             :       // Figure out mailcite ancestors
    2408           0 :       nsCOMPtr<Element> startCiteNode = GetTopEnclosingMailCite(*startNode);
    2409           0 :       nsCOMPtr<Element> endCiteNode = GetTopEnclosingMailCite(*endNode);
    2410             : 
    2411             :       // If we only have a mailcite at one of the two endpoints, set the
    2412             :       // directionality of the deletion so that the selection will end up
    2413             :       // outside the mailcite.
    2414           0 :       if (startCiteNode && !endCiteNode) {
    2415           0 :         aAction = nsIEditor::eNext;
    2416           0 :       } else if (!startCiteNode && endCiteNode) {
    2417           0 :         aAction = nsIEditor::ePrevious;
    2418             :       }
    2419             : 
    2420             :       // Figure out block parents
    2421           0 :       NS_ENSURE_STATE(mHTMLEditor);
    2422           0 :       nsCOMPtr<Element> leftParent = mHTMLEditor->GetBlock(*startNode);
    2423           0 :       nsCOMPtr<Element> rightParent = mHTMLEditor->GetBlock(*endNode);
    2424             : 
    2425             :       // Are endpoint block parents the same?  Use default deletion
    2426           0 :       if (leftParent && leftParent == rightParent) {
    2427           0 :         NS_ENSURE_STATE(mHTMLEditor);
    2428           0 :         mHTMLEditor->DeleteSelectionImpl(aAction, aStripWrappers);
    2429             :       } else {
    2430             :         // Deleting across blocks.  Are the blocks of same type?
    2431           0 :         NS_ENSURE_STATE(leftParent && rightParent);
    2432             : 
    2433             :         // Are the blocks siblings?
    2434           0 :         nsCOMPtr<nsINode> leftBlockParent = leftParent->GetParentNode();
    2435           0 :         nsCOMPtr<nsINode> rightBlockParent = rightParent->GetParentNode();
    2436             : 
    2437             :         // MOOSE: this could conceivably screw up a table.. fix me.
    2438           0 :         NS_ENSURE_STATE(mHTMLEditor);
    2439           0 :         if (leftBlockParent == rightBlockParent &&
    2440           0 :             mHTMLEditor->NodesSameType(GetAsDOMNode(leftParent),
    2441           0 :                                        GetAsDOMNode(rightParent)) &&
    2442             :             // XXX What's special about these three types of block?
    2443           0 :             (leftParent->IsHTMLElement(nsGkAtoms::p) ||
    2444           0 :              HTMLEditUtils::IsListItem(leftParent) ||
    2445           0 :              HTMLEditUtils::IsHeader(*leftParent))) {
    2446             :           // First delete the selection
    2447           0 :           NS_ENSURE_STATE(mHTMLEditor);
    2448           0 :           rv = mHTMLEditor->DeleteSelectionImpl(aAction, aStripWrappers);
    2449           0 :           NS_ENSURE_SUCCESS(rv, rv);
    2450             :           // Join blocks
    2451           0 :           NS_ENSURE_STATE(mHTMLEditor);
    2452             :           EditorDOMPoint pt =
    2453           0 :             mHTMLEditor->JoinNodeDeep(*leftParent, *rightParent);
    2454           0 :           NS_ENSURE_STATE(pt.node);
    2455             :           // Fix up selection
    2456           0 :           rv = aSelection->Collapse(pt.node, pt.offset);
    2457           0 :           NS_ENSURE_SUCCESS(rv, rv);
    2458           0 :           return NS_OK;
    2459             :         }
    2460             : 
    2461             :         // Else blocks not same type, or not siblings.  Delete everything
    2462             :         // except table elements.
    2463           0 :         join = true;
    2464             : 
    2465           0 :         AutoRangeArray arrayOfRanges(aSelection);
    2466           0 :         for (auto& range : arrayOfRanges.mRanges) {
    2467             :           // Build a list of nodes in the range
    2468           0 :           nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
    2469           0 :           TrivialFunctor functor;
    2470           0 :           DOMSubtreeIterator iter;
    2471           0 :           nsresult rv = iter.Init(*range);
    2472           0 :           NS_ENSURE_SUCCESS(rv, rv);
    2473           0 :           iter.AppendList(functor, arrayOfNodes);
    2474             : 
    2475             :           // Now that we have the list, delete non-table elements
    2476           0 :           int32_t listCount = arrayOfNodes.Length();
    2477           0 :           for (int32_t j = 0; j < listCount; j++) {
    2478           0 :             nsCOMPtr<nsINode> somenode = do_QueryInterface(arrayOfNodes[0]);
    2479           0 :             NS_ENSURE_STATE(somenode);
    2480           0 :             DeleteNonTableElements(somenode);
    2481           0 :             arrayOfNodes.RemoveElementAt(0);
    2482             :             // If something visible is deleted, no need to join.  Visible means
    2483             :             // all nodes except non-visible textnodes and breaks.
    2484           0 :             if (join && origCollapsed) {
    2485           0 :               if (!somenode->IsContent()) {
    2486           0 :                 join = false;
    2487           0 :                 continue;
    2488             :               }
    2489           0 :               nsCOMPtr<nsIContent> content = somenode->AsContent();
    2490           0 :               if (content->NodeType() == nsIDOMNode::TEXT_NODE) {
    2491           0 :                 NS_ENSURE_STATE(mHTMLEditor);
    2492           0 :                 mHTMLEditor->IsVisTextNode(content, &join, true);
    2493             :               } else {
    2494           0 :                 NS_ENSURE_STATE(mHTMLEditor);
    2495           0 :                 join = content->IsHTMLElement(nsGkAtoms::br) &&
    2496           0 :                        !mHTMLEditor->IsVisBreak(somenode);
    2497             :               }
    2498             :             }
    2499             :           }
    2500             :         }
    2501             : 
    2502             :         // Check endpoints for possible text deletion.  We can assume that if
    2503             :         // text node is found, we can delete to end or to begining as
    2504             :         // appropriate, since the case where both sel endpoints in same text
    2505             :         // node was already handled (we wouldn't be here)
    2506           0 :         if (startNode->GetAsText() &&
    2507           0 :             startNode->Length() > static_cast<uint32_t>(startOffset)) {
    2508             :           // Delete to last character
    2509             :           OwningNonNull<nsGenericDOMDataNode> dataNode =
    2510           0 :             *static_cast<nsGenericDOMDataNode*>(startNode.get());
    2511           0 :           NS_ENSURE_STATE(mHTMLEditor);
    2512           0 :           rv = mHTMLEditor->DeleteText(dataNode, startOffset,
    2513           0 :                                        startNode->Length() - startOffset);
    2514           0 :           NS_ENSURE_SUCCESS(rv, rv);
    2515             :         }
    2516           0 :         if (endNode->GetAsText() && endOffset) {
    2517             :           // Delete to first character
    2518           0 :           NS_ENSURE_STATE(mHTMLEditor);
    2519             :           OwningNonNull<nsGenericDOMDataNode> dataNode =
    2520           0 :             *static_cast<nsGenericDOMDataNode*>(endNode.get());
    2521           0 :           rv = mHTMLEditor->DeleteText(dataNode, 0, endOffset);
    2522           0 :           NS_ENSURE_SUCCESS(rv, rv);
    2523             :         }
    2524             : 
    2525           0 :         if (join) {
    2526           0 :           EditActionResult ret = TryToJoinBlocks(*leftParent, *rightParent);
    2527           0 :           MOZ_ASSERT(*aHandled);
    2528           0 :           *aCancel |= ret.Canceled();
    2529           0 :           if (NS_WARN_IF(ret.Failed())) {
    2530           0 :             return ret.Rv();
    2531             :           }
    2532             :         }
    2533             :       }
    2534             :     }
    2535             :   }
    2536             : 
    2537             :   // We might have left only collapsed whitespace in the start/end nodes
    2538             :   {
    2539           0 :     AutoTrackDOMPoint startTracker(mHTMLEditor->mRangeUpdater,
    2540           0 :                                    address_of(startNode), &startOffset);
    2541           0 :     AutoTrackDOMPoint endTracker(mHTMLEditor->mRangeUpdater,
    2542           0 :                                  address_of(endNode), &endOffset);
    2543             : 
    2544           0 :     DeleteNodeIfCollapsedText(*startNode);
    2545           0 :     DeleteNodeIfCollapsedText(*endNode);
    2546             :   }
    2547             : 
    2548             :   // If we're joining blocks: if deleting forward the selection should be
    2549             :   // collapsed to the end of the selection, if deleting backward the selection
    2550             :   // should be collapsed to the beginning of the selection. But if we're not
    2551             :   // joining then the selection should collapse to the beginning of the
    2552             :   // selection if we'redeleting forward, because the end of the selection will
    2553             :   // still be in the next block. And same thing for deleting backwards
    2554             :   // (selection should collapse to the end, because the beginning will still be
    2555             :   // in the first block). See Bug 507936
    2556           0 :   if (aAction == (join ? nsIEditor::eNext : nsIEditor::ePrevious)) {
    2557           0 :     rv = aSelection->Collapse(endNode, endOffset);
    2558           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2559           0 :       return rv;
    2560             :     }
    2561             :   } else {
    2562           0 :     rv = aSelection->Collapse(startNode, startOffset);
    2563           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2564           0 :       return rv;
    2565             :     }
    2566             :   }
    2567           0 :   return NS_OK;
    2568             : }
    2569             : 
    2570             : /**
    2571             :  * If aNode is a text node that contains only collapsed whitespace, delete it.
    2572             :  * It doesn't serve any useful purpose, and we don't want it to confuse code
    2573             :  * that doesn't correctly skip over it.
    2574             :  *
    2575             :  * If deleting the node fails (like if it's not editable), the caller should
    2576             :  * proceed as usual, so don't return any errors.
    2577             :  */
    2578             : void
    2579           0 : HTMLEditRules::DeleteNodeIfCollapsedText(nsINode& aNode)
    2580             : {
    2581           0 :   if (!aNode.GetAsText()) {
    2582           0 :     return;
    2583             :   }
    2584             :   bool empty;
    2585           0 :   nsresult rv = mHTMLEditor->IsVisTextNode(aNode.AsContent(), &empty, false);
    2586           0 :   NS_ENSURE_SUCCESS_VOID(rv);
    2587           0 :   if (empty) {
    2588           0 :     mHTMLEditor->DeleteNode(&aNode);
    2589             :   }
    2590             : }
    2591             : 
    2592             : 
    2593             : /**
    2594             :  * InsertBRIfNeeded() determines if a br is needed for current selection to not
    2595             :  * be spastic.  If so, it inserts one.  Callers responsibility to only call
    2596             :  * with collapsed selection.
    2597             :  *
    2598             :  * @param aSelection        The collapsed selection.
    2599             :  */
    2600             : nsresult
    2601           0 : HTMLEditRules::InsertBRIfNeeded(Selection* aSelection)
    2602             : {
    2603           0 :   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
    2604             : 
    2605             :   // get selection
    2606           0 :   nsCOMPtr<nsINode> node;
    2607             :   int32_t offset;
    2608             :   nsresult rv =
    2609           0 :     EditorBase::GetStartNodeAndOffset(aSelection,
    2610           0 :                                       getter_AddRefs(node), &offset);
    2611           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2612           0 :   NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
    2613             : 
    2614             :   // inline elements don't need any br
    2615           0 :   if (!IsBlockNode(*node)) {
    2616           0 :     return NS_OK;
    2617             :   }
    2618             : 
    2619             :   // examine selection
    2620           0 :   NS_ENSURE_STATE(mHTMLEditor);
    2621           0 :   WSRunObject wsObj(mHTMLEditor, node, offset);
    2622           0 :   if (((wsObj.mStartReason & WSType::block) ||
    2623           0 :        (wsObj.mStartReason & WSType::br)) &&
    2624           0 :       (wsObj.mEndReason & WSType::block)) {
    2625             :     // if we are tucked between block boundaries then insert a br
    2626             :     // first check that we are allowed to
    2627           0 :     NS_ENSURE_STATE(mHTMLEditor);
    2628           0 :     if (mHTMLEditor->CanContainTag(*node, *nsGkAtoms::br)) {
    2629           0 :       NS_ENSURE_STATE(mHTMLEditor);
    2630             :       nsCOMPtr<Element> br =
    2631           0 :         mHTMLEditor->CreateBR(node, offset, nsIEditor::ePrevious);
    2632           0 :       return br ? NS_OK : NS_ERROR_FAILURE;
    2633             :     }
    2634             :   }
    2635           0 :   return NS_OK;
    2636             : }
    2637             : 
    2638             : /**
    2639             :  * GetGoodSelPointForNode() finds where at a node you would want to set the
    2640             :  * selection if you were trying to have a caret next to it.  Always returns a
    2641             :  * valid value (unless mHTMLEditor has gone away).
    2642             :  *
    2643             :  * @param aNode         The node
    2644             :  * @param aAction       Which edge to find:
    2645             :  *                        eNext/eNextWord/eToEndOfLine indicates beginning,
    2646             :  *                        ePrevious/PreviousWord/eToBeginningOfLine ending.
    2647             :  */
    2648             : EditorDOMPoint
    2649           0 : HTMLEditRules::GetGoodSelPointForNode(nsINode& aNode,
    2650             :                                       nsIEditor::EDirection aAction)
    2651             : {
    2652           0 :   MOZ_ASSERT(aAction == nsIEditor::eNext ||
    2653             :              aAction == nsIEditor::eNextWord ||
    2654             :              aAction == nsIEditor::ePrevious ||
    2655             :              aAction == nsIEditor::ePreviousWord ||
    2656             :              aAction == nsIEditor::eToBeginningOfLine ||
    2657             :              aAction == nsIEditor::eToEndOfLine);
    2658             : 
    2659           0 :   bool isPreviousAction = (aAction == nsIEditor::ePrevious ||
    2660           0 :                            aAction == nsIEditor::ePreviousWord ||
    2661           0 :                            aAction == nsIEditor::eToBeginningOfLine);
    2662             : 
    2663           0 :   NS_ENSURE_TRUE(mHTMLEditor, EditorDOMPoint());
    2664           0 :   if (aNode.GetAsText() || mHTMLEditor->IsContainer(&aNode) ||
    2665           0 :       NS_WARN_IF(!aNode.GetParentNode())) {
    2666           0 :     return EditorDOMPoint(&aNode, isPreviousAction ? aNode.Length() : 0);
    2667             :   }
    2668             : 
    2669           0 :   EditorDOMPoint ret;
    2670           0 :   ret.node = aNode.GetParentNode();
    2671           0 :   ret.offset = ret.node ? ret.node->IndexOf(&aNode) : -1;
    2672           0 :   NS_ENSURE_TRUE(mHTMLEditor, EditorDOMPoint());
    2673           0 :   if ((!aNode.IsHTMLElement(nsGkAtoms::br) ||
    2674           0 :        mHTMLEditor->IsVisBreak(&aNode)) && isPreviousAction) {
    2675           0 :     ret.offset++;
    2676             :   }
    2677           0 :   return ret;
    2678             : }
    2679             : 
    2680             : EditActionResult
    2681           0 : HTMLEditRules::TryToJoinBlocks(nsIContent& aLeftNode,
    2682             :                                nsIContent& aRightNode)
    2683             : {
    2684           0 :   if (NS_WARN_IF(!mHTMLEditor)) {
    2685           0 :     return EditActionIgnored(NS_ERROR_UNEXPECTED);
    2686             :   }
    2687             : 
    2688           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    2689             : 
    2690           0 :   nsCOMPtr<Element> leftBlock = htmlEditor->GetBlock(aLeftNode);
    2691           0 :   nsCOMPtr<Element> rightBlock = htmlEditor->GetBlock(aRightNode);
    2692             : 
    2693             :   // Sanity checks
    2694           0 :   if (NS_WARN_IF(!leftBlock) || NS_WARN_IF(!rightBlock)) {
    2695           0 :     return EditActionIgnored(NS_ERROR_NULL_POINTER);
    2696             :   }
    2697           0 :   if (NS_WARN_IF(leftBlock == rightBlock)) {
    2698           0 :     return EditActionIgnored(NS_ERROR_UNEXPECTED);
    2699             :   }
    2700             : 
    2701           0 :   if (HTMLEditUtils::IsTableElement(leftBlock) ||
    2702           0 :       HTMLEditUtils::IsTableElement(rightBlock)) {
    2703             :     // Do not try to merge table elements
    2704           0 :     return EditActionCanceled();
    2705             :   }
    2706             : 
    2707             :   // Make sure we don't try to move things into HR's, which look like blocks
    2708             :   // but aren't containers
    2709           0 :   if (leftBlock->IsHTMLElement(nsGkAtoms::hr)) {
    2710           0 :     leftBlock = htmlEditor->GetBlockNodeParent(leftBlock);
    2711           0 :     if (NS_WARN_IF(!leftBlock)) {
    2712           0 :       return EditActionIgnored(NS_ERROR_UNEXPECTED);
    2713             :     }
    2714             :   }
    2715           0 :   if (rightBlock->IsHTMLElement(nsGkAtoms::hr)) {
    2716           0 :     rightBlock = htmlEditor->GetBlockNodeParent(rightBlock);
    2717           0 :     if (NS_WARN_IF(!rightBlock)) {
    2718           0 :       return EditActionIgnored(NS_ERROR_UNEXPECTED);
    2719             :     }
    2720             :   }
    2721             : 
    2722             :   // Bail if both blocks the same
    2723           0 :   if (leftBlock == rightBlock) {
    2724           0 :     return EditActionIgnored();
    2725             :   }
    2726             : 
    2727             :   // Joining a list item to its parent is a NOP.
    2728           0 :   if (HTMLEditUtils::IsList(leftBlock) &&
    2729           0 :       HTMLEditUtils::IsListItem(rightBlock) &&
    2730           0 :       rightBlock->GetParentNode() == leftBlock) {
    2731           0 :     return EditActionHandled();
    2732             :   }
    2733             : 
    2734             :   // Special rule here: if we are trying to join list items, and they are in
    2735             :   // different lists, join the lists instead.
    2736           0 :   bool mergeLists = false;
    2737           0 :   nsIAtom* existingList = nsGkAtoms::_empty;
    2738             :   int32_t offset;
    2739           0 :   nsCOMPtr<Element> leftList, rightList;
    2740           0 :   if (HTMLEditUtils::IsListItem(leftBlock) &&
    2741           0 :       HTMLEditUtils::IsListItem(rightBlock)) {
    2742           0 :     leftList = leftBlock->GetParentElement();
    2743           0 :     rightList = rightBlock->GetParentElement();
    2744           0 :     if (leftList && rightList && leftList != rightList &&
    2745           0 :         !EditorUtils::IsDescendantOf(leftList, rightBlock, &offset) &&
    2746           0 :         !EditorUtils::IsDescendantOf(rightList, leftBlock, &offset)) {
    2747             :       // There are some special complications if the lists are descendants of
    2748             :       // the other lists' items.  Note that it is okay for them to be
    2749             :       // descendants of the other lists themselves, which is the usual case for
    2750             :       // sublists in our implementation.
    2751           0 :       leftBlock = leftList;
    2752           0 :       rightBlock = rightList;
    2753           0 :       mergeLists = true;
    2754           0 :       existingList = leftList->NodeInfo()->NameAtom();
    2755             :     }
    2756             :   }
    2757             : 
    2758           0 :   AutoTransactionsConserveSelection dontSpazMySelection(htmlEditor);
    2759             : 
    2760           0 :   int32_t rightOffset = 0;
    2761           0 :   int32_t leftOffset = -1;
    2762             : 
    2763             :   // offset below is where you find yourself in rightBlock when you traverse
    2764             :   // upwards from leftBlock
    2765           0 :   if (EditorUtils::IsDescendantOf(leftBlock, rightBlock, &rightOffset)) {
    2766             :     // Tricky case.  Left block is inside right block.  Do ws adjustment.  This
    2767             :     // just destroys non-visible ws at boundaries we will be joining.
    2768           0 :     rightOffset++;
    2769           0 :     nsresult rv = WSRunObject::ScrubBlockBoundary(htmlEditor,
    2770             :                                                   WSRunObject::kBlockEnd,
    2771           0 :                                                   leftBlock);
    2772           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2773           0 :       return EditActionIgnored(rv);
    2774             :     }
    2775             : 
    2776             :     {
    2777             :       // We can't just track rightBlock because it's an Element.
    2778           0 :       nsCOMPtr<nsINode> trackingRightBlock(rightBlock);
    2779           0 :       AutoTrackDOMPoint tracker(htmlEditor->mRangeUpdater,
    2780           0 :                                 address_of(trackingRightBlock), &rightOffset);
    2781           0 :       rv = WSRunObject::ScrubBlockBoundary(htmlEditor,
    2782             :                                            WSRunObject::kAfterBlock,
    2783           0 :                                            rightBlock, rightOffset);
    2784           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2785           0 :         return EditActionIgnored(rv);
    2786             :       }
    2787             : 
    2788           0 :       if (trackingRightBlock->IsElement()) {
    2789           0 :         rightBlock = trackingRightBlock->AsElement();
    2790             :       } else {
    2791           0 :         if (NS_WARN_IF(!trackingRightBlock->GetParentElement())) {
    2792           0 :           return EditActionIgnored(NS_ERROR_UNEXPECTED);
    2793             :         }
    2794           0 :         rightBlock = trackingRightBlock->GetParentElement();
    2795             :       }
    2796             :     }
    2797             :     // Do br adjustment.
    2798             :     nsCOMPtr<Element> brNode =
    2799           0 :       CheckForInvisibleBR(*leftBlock, BRLocation::blockEnd);
    2800           0 :     EditActionResult ret(NS_OK);
    2801           0 :     if (mergeLists) {
    2802             :       // The idea here is to take all children in rightList that are past
    2803             :       // offset, and pull them into leftlist.
    2804           0 :       for (nsCOMPtr<nsIContent> child = rightList->GetChildAt(offset);
    2805           0 :            child; child = rightList->GetChildAt(rightOffset)) {
    2806           0 :         rv = htmlEditor->MoveNode(child, leftList, -1);
    2807           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    2808           0 :           return EditActionIgnored(rv);
    2809             :         }
    2810             :       }
    2811             :       // XXX Should this set to true only when above for loop moves the node?
    2812           0 :       ret.MarkAsHandled();
    2813             :     } else {
    2814             :       // XXX Why do we ignore the result of MoveBlock()?
    2815             :       EditActionResult retMoveBlock =
    2816           0 :         MoveBlock(*leftBlock, *rightBlock, leftOffset, rightOffset);
    2817           0 :       if (retMoveBlock.Handled()) {
    2818           0 :         ret.MarkAsHandled();
    2819             :       }
    2820             :     }
    2821           0 :     if (brNode && NS_SUCCEEDED(htmlEditor->DeleteNode(brNode))) {
    2822           0 :       ret.MarkAsHandled();
    2823             :     }
    2824           0 :     return ret;
    2825             :   }
    2826             : 
    2827             :   // Offset below is where you find yourself in leftBlock when you traverse
    2828             :   // upwards from rightBlock
    2829           0 :   if (EditorUtils::IsDescendantOf(rightBlock, leftBlock, &leftOffset)) {
    2830             :     // Tricky case.  Right block is inside left block.  Do ws adjustment.  This
    2831             :     // just destroys non-visible ws at boundaries we will be joining.
    2832           0 :     nsresult rv = WSRunObject::ScrubBlockBoundary(htmlEditor,
    2833             :                                                   WSRunObject::kBlockStart,
    2834           0 :                                                   rightBlock);
    2835           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2836           0 :       return EditActionIgnored(rv);
    2837             :     }
    2838             : 
    2839             :     {
    2840             :       // We can't just track leftBlock because it's an Element, so track
    2841             :       // something else.
    2842           0 :       nsCOMPtr<nsINode> trackingLeftBlock(leftBlock);
    2843           0 :       AutoTrackDOMPoint tracker(htmlEditor->mRangeUpdater,
    2844           0 :                                 address_of(trackingLeftBlock), &leftOffset);
    2845           0 :       rv = WSRunObject::ScrubBlockBoundary(htmlEditor,
    2846             :                                            WSRunObject::kBeforeBlock,
    2847           0 :                                            leftBlock, leftOffset);
    2848           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2849           0 :         return EditActionIgnored(rv);
    2850             :       }
    2851             : 
    2852           0 :       if (trackingLeftBlock->IsElement()) {
    2853           0 :         leftBlock = trackingLeftBlock->AsElement();
    2854             :       } else {
    2855           0 :         if (NS_WARN_IF(!trackingLeftBlock->GetParentElement())) {
    2856           0 :           return EditActionIgnored(NS_ERROR_UNEXPECTED);
    2857             :         }
    2858           0 :         leftBlock = trackingLeftBlock->GetParentElement();
    2859             :       }
    2860             :     }
    2861             :     // Do br adjustment.
    2862             :     nsCOMPtr<Element> brNode =
    2863           0 :       CheckForInvisibleBR(*leftBlock, BRLocation::beforeBlock, leftOffset);
    2864           0 :     EditActionResult ret(NS_OK);
    2865           0 :     if (mergeLists) {
    2866             :       // XXX Why do we ignore the result of MoveContents()?
    2867             :       EditActionResult retMoveContents =
    2868           0 :         MoveContents(*rightList, *leftList, &leftOffset);
    2869           0 :       if (retMoveContents.Handled()) {
    2870           0 :         ret.MarkAsHandled();
    2871             :       }
    2872             :     } else {
    2873             :       // Left block is a parent of right block, and the parent of the previous
    2874             :       // visible content.  Right block is a child and contains the contents we
    2875             :       // want to move.
    2876             : 
    2877             :       int32_t previousContentOffset;
    2878           0 :       nsCOMPtr<nsINode> previousContentParent;
    2879             : 
    2880           0 :       if (&aLeftNode == leftBlock) {
    2881             :         // We are working with valid HTML, aLeftNode is a block node, and is
    2882             :         // therefore allowed to contain rightBlock.  This is the simple case,
    2883             :         // we will simply move the content in rightBlock out of its block.
    2884           0 :         previousContentParent = leftBlock;
    2885           0 :         previousContentOffset = leftOffset;
    2886             :       } else {
    2887             :         // We try to work as well as possible with HTML that's already invalid.
    2888             :         // Although "right block" is a block, and a block must not be contained
    2889             :         // in inline elements, reality is that broken documents do exist.  The
    2890             :         // DIRECT parent of "left NODE" might be an inline element.  Previous
    2891             :         // versions of this code skipped inline parents until the first block
    2892             :         // parent was found (and used "left block" as the destination).
    2893             :         // However, in some situations this strategy moves the content to an
    2894             :         // unexpected position.  (see bug 200416) The new idea is to make the
    2895             :         // moving content a sibling, next to the previous visible content.
    2896             : 
    2897           0 :         previousContentParent = aLeftNode.GetParentNode();
    2898           0 :         previousContentOffset = previousContentParent ?
    2899           0 :           previousContentParent->IndexOf(&aLeftNode) : -1;
    2900             : 
    2901             :         // We want to move our content just after the previous visible node.
    2902           0 :         previousContentOffset++;
    2903             :       }
    2904             : 
    2905             :       // Because we don't want the moving content to receive the style of the
    2906             :       // previous content, we split the previous content's style.
    2907             : 
    2908           0 :       nsCOMPtr<Element> editorRoot = htmlEditor->GetEditorRoot();
    2909           0 :       if (!editorRoot || &aLeftNode != editorRoot) {
    2910           0 :         nsCOMPtr<nsIContent> splittedPreviousContent;
    2911           0 :         rv = htmlEditor->SplitStyleAbovePoint(
    2912             :                            address_of(previousContentParent),
    2913             :                            &previousContentOffset,
    2914             :                            nullptr, nullptr, nullptr,
    2915           0 :                            getter_AddRefs(splittedPreviousContent));
    2916           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    2917           0 :           return EditActionIgnored(rv);
    2918             :         }
    2919             : 
    2920           0 :         if (splittedPreviousContent) {
    2921           0 :           previousContentParent = splittedPreviousContent->GetParentNode();
    2922           0 :           previousContentOffset = previousContentParent ?
    2923           0 :             previousContentParent->IndexOf(splittedPreviousContent) : -1;
    2924             :         }
    2925             :       }
    2926             : 
    2927           0 :       if (NS_WARN_IF(!previousContentParent)) {
    2928           0 :         return EditActionIgnored(NS_ERROR_NULL_POINTER);
    2929             :       }
    2930             : 
    2931           0 :       ret |= MoveBlock(*previousContentParent->AsElement(), *rightBlock,
    2932           0 :                        previousContentOffset, rightOffset);
    2933           0 :       if (NS_WARN_IF(ret.Failed())) {
    2934           0 :         return ret;
    2935             :       }
    2936             :     }
    2937           0 :     if (brNode && NS_SUCCEEDED(htmlEditor->DeleteNode(brNode))) {
    2938           0 :       ret.MarkAsHandled();
    2939             :     }
    2940           0 :     return ret;
    2941             :   }
    2942             : 
    2943             :   // Normal case.  Blocks are siblings, or at least close enough.  An example
    2944             :   // of the latter is <p>paragraph</p><ul><li>one<li>two<li>three</ul>.  The
    2945             :   // first li and the p are not true siblings, but we still want to join them
    2946             :   // if you backspace from li into p.
    2947             : 
    2948             :   // Adjust whitespace at block boundaries
    2949             :   nsresult rv =
    2950           0 :     WSRunObject::PrepareToJoinBlocks(htmlEditor, leftBlock, rightBlock);
    2951           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2952           0 :     return EditActionIgnored(rv);
    2953             :   }
    2954             :   // Do br adjustment.
    2955             :   nsCOMPtr<Element> brNode =
    2956           0 :     CheckForInvisibleBR(*leftBlock, BRLocation::blockEnd);
    2957           0 :   EditActionResult ret(NS_OK);
    2958           0 :   if (mergeLists || leftBlock->NodeInfo()->NameAtom() ==
    2959           0 :                     rightBlock->NodeInfo()->NameAtom()) {
    2960             :     // Nodes are same type.  merge them.
    2961           0 :     EditorDOMPoint pt = JoinNodesSmart(*leftBlock, *rightBlock);
    2962           0 :     if (pt.node && mergeLists) {
    2963           0 :       nsCOMPtr<Element> newBlock;
    2964           0 :       ConvertListType(rightBlock, getter_AddRefs(newBlock),
    2965           0 :                       existingList, nsGkAtoms::li);
    2966             :     }
    2967           0 :     ret.MarkAsHandled();
    2968             :   } else {
    2969             :     // Nodes are dissimilar types.
    2970           0 :     ret |= MoveBlock(*leftBlock, *rightBlock, leftOffset, rightOffset);
    2971           0 :     if (NS_WARN_IF(ret.Failed())) {
    2972           0 :       return ret;
    2973             :     }
    2974             :   }
    2975           0 :   if (brNode) {
    2976           0 :     rv = htmlEditor->DeleteNode(brNode);
    2977             :     // XXX In other top level if blocks, the result of DeleteNode()
    2978             :     //     is ignored.  Why does only this result is respected?
    2979           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2980           0 :       return ret.SetResult(rv);
    2981             :     }
    2982           0 :     ret.MarkAsHandled();
    2983             :   }
    2984           0 :   return ret;
    2985             : }
    2986             : 
    2987             : EditActionResult
    2988           0 : HTMLEditRules::MoveBlock(Element& aLeftBlock,
    2989             :                          Element& aRightBlock,
    2990             :                          int32_t aLeftOffset,
    2991             :                          int32_t aRightOffset)
    2992             : {
    2993           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
    2994             :   // GetNodesFromPoint is the workhorse that figures out what we wnat to move.
    2995           0 :   nsresult rv = GetNodesFromPoint(EditorDOMPoint(&aRightBlock, aRightOffset),
    2996             :                                   EditAction::makeList, arrayOfNodes,
    2997           0 :                                   TouchContent::yes);
    2998           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2999           0 :     return EditActionIgnored(rv);
    3000             :   }
    3001             : 
    3002           0 :   EditActionResult ret(NS_OK);
    3003           0 :   for (uint32_t i = 0; i < arrayOfNodes.Length(); i++) {
    3004             :     // get the node to act on
    3005           0 :     if (IsBlockNode(arrayOfNodes[i])) {
    3006             :       // For block nodes, move their contents only, then delete block.
    3007             :       ret |=
    3008           0 :         MoveContents(*arrayOfNodes[i]->AsElement(), aLeftBlock, &aLeftOffset);
    3009           0 :       if (NS_WARN_IF(ret.Failed())) {
    3010           0 :         return ret;
    3011             :       }
    3012           0 :       if (NS_WARN_IF(!mHTMLEditor)) {
    3013           0 :         return ret.SetResult(NS_ERROR_UNEXPECTED);
    3014             :       }
    3015           0 :       rv = mHTMLEditor->DeleteNode(arrayOfNodes[i]);
    3016           0 :       ret.MarkAsHandled();
    3017             :     } else {
    3018             :       // Otherwise move the content as is, checking against the DTD.
    3019             :       ret |=
    3020           0 :         MoveNodeSmart(*arrayOfNodes[i]->AsContent(), aLeftBlock, &aLeftOffset);
    3021             :     }
    3022             :   }
    3023             : 
    3024             :   // XXX We're only checking return value of the last iteration
    3025           0 :   if (NS_WARN_IF(ret.Failed())) {
    3026           0 :     return ret;
    3027             :   }
    3028             : 
    3029           0 :   return ret;
    3030             : }
    3031             : 
    3032             : EditActionResult
    3033           0 : HTMLEditRules::MoveNodeSmart(nsIContent& aNode,
    3034             :                              Element& aDestElement,
    3035             :                              int32_t* aInOutDestOffset)
    3036             : {
    3037           0 :   MOZ_ASSERT(aInOutDestOffset);
    3038             : 
    3039           0 :   if (NS_WARN_IF(!mHTMLEditor)) {
    3040           0 :     return EditActionIgnored(NS_ERROR_UNEXPECTED);
    3041             :   }
    3042             : 
    3043           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    3044             : 
    3045             :   // Check if this node can go into the destination node
    3046           0 :   if (htmlEditor->CanContain(aDestElement, aNode)) {
    3047             :     // If it can, move it there
    3048             :     nsresult rv =
    3049           0 :       htmlEditor->MoveNode(&aNode, &aDestElement, *aInOutDestOffset);
    3050           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    3051           0 :       return EditActionIgnored(rv);
    3052             :     }
    3053           0 :     if (*aInOutDestOffset != -1) {
    3054           0 :       (*aInOutDestOffset)++;
    3055             :     }
    3056             :     // XXX Should we check if the node is actually moved in this case?
    3057           0 :     return EditActionHandled();
    3058             :   }
    3059             : 
    3060             :   // If it can't, move its children (if any), and then delete it.
    3061           0 :   EditActionResult ret(NS_OK);
    3062           0 :   if (aNode.IsElement()) {
    3063           0 :     ret = MoveContents(*aNode.AsElement(), aDestElement, aInOutDestOffset);
    3064           0 :     if (NS_WARN_IF(ret.Failed())) {
    3065           0 :       return ret;
    3066             :     }
    3067             :   }
    3068             : 
    3069           0 :   nsresult rv = htmlEditor->DeleteNode(&aNode);
    3070           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3071           0 :     return ret.SetResult(rv);
    3072             :   }
    3073           0 :   return ret.MarkAsHandled();
    3074             : }
    3075             : 
    3076             : EditActionResult
    3077           0 : HTMLEditRules::MoveContents(Element& aElement,
    3078             :                             Element& aDestElement,
    3079             :                             int32_t* aInOutDestOffset)
    3080             : {
    3081           0 :   MOZ_ASSERT(aInOutDestOffset);
    3082             : 
    3083           0 :   if (NS_WARN_IF(&aElement == &aDestElement)) {
    3084           0 :     return EditActionIgnored(NS_ERROR_ILLEGAL_VALUE);
    3085             :   }
    3086             : 
    3087           0 :   EditActionResult ret(NS_OK);
    3088           0 :   while (aElement.GetFirstChild()) {
    3089             :     ret |=
    3090           0 :       MoveNodeSmart(*aElement.GetFirstChild(), aDestElement, aInOutDestOffset);
    3091           0 :     if (NS_WARN_IF(ret.Failed())) {
    3092           0 :       return ret;
    3093             :     }
    3094             :   }
    3095           0 :   return ret;
    3096             : }
    3097             : 
    3098             : 
    3099             : nsresult
    3100           0 : HTMLEditRules::DeleteNonTableElements(nsINode* aNode)
    3101             : {
    3102           0 :   MOZ_ASSERT(aNode);
    3103           0 :   if (!HTMLEditUtils::IsTableElementButNotTable(aNode)) {
    3104           0 :     NS_ENSURE_STATE(mHTMLEditor);
    3105           0 :     return mHTMLEditor->DeleteNode(aNode->AsDOMNode());
    3106             :   }
    3107             : 
    3108           0 :   for (int32_t i = aNode->GetChildCount() - 1; i >= 0; --i) {
    3109           0 :     nsresult rv = DeleteNonTableElements(aNode->GetChildAt(i));
    3110           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3111             :   }
    3112           0 :   return NS_OK;
    3113             : }
    3114             : 
    3115             : nsresult
    3116           0 : HTMLEditRules::DidDeleteSelection(Selection* aSelection,
    3117             :                                   nsIEditor::EDirection aDir,
    3118             :                                   nsresult aResult)
    3119             : {
    3120           0 :   if (!aSelection) {
    3121           0 :     return NS_ERROR_NULL_POINTER;
    3122             :   }
    3123             : 
    3124             :   // find where we are
    3125           0 :   nsCOMPtr<nsINode> startNode;
    3126             :   int32_t startOffset;
    3127           0 :   nsresult rv = EditorBase::GetStartNodeAndOffset(aSelection,
    3128           0 :                                                   getter_AddRefs(startNode),
    3129           0 :                                                   &startOffset);
    3130           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3131           0 :   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
    3132             : 
    3133             :   // find any enclosing mailcite
    3134           0 :   nsCOMPtr<Element> citeNode = GetTopEnclosingMailCite(*startNode);
    3135           0 :   if (citeNode) {
    3136           0 :     bool isEmpty = true, seenBR = false;
    3137           0 :     NS_ENSURE_STATE(mHTMLEditor);
    3138           0 :     mHTMLEditor->IsEmptyNodeImpl(citeNode, &isEmpty, true, true, false,
    3139           0 :                                  &seenBR);
    3140           0 :     if (isEmpty) {
    3141             :       int32_t offset;
    3142           0 :       nsCOMPtr<nsINode> parent = EditorBase::GetNodeLocation(citeNode, &offset);
    3143           0 :       NS_ENSURE_STATE(mHTMLEditor);
    3144           0 :       rv = mHTMLEditor->DeleteNode(citeNode);
    3145           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3146           0 :       if (parent && seenBR) {
    3147           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3148           0 :         nsCOMPtr<Element> brNode = mHTMLEditor->CreateBR(parent, offset);
    3149           0 :         NS_ENSURE_STATE(brNode);
    3150           0 :         aSelection->Collapse(parent, offset);
    3151             :       }
    3152             :     }
    3153             :   }
    3154             : 
    3155             :   // call through to base class
    3156           0 :   return TextEditRules::DidDeleteSelection(aSelection, aDir, aResult);
    3157             : }
    3158             : 
    3159             : nsresult
    3160           0 : HTMLEditRules::WillMakeList(Selection* aSelection,
    3161             :                             const nsAString* aListType,
    3162             :                             bool aEntireList,
    3163             :                             const nsAString* aBulletType,
    3164             :                             bool* aCancel,
    3165             :                             bool* aHandled,
    3166             :                             const nsAString* aItemType)
    3167             : {
    3168           0 :   if (!aSelection || !aListType || !aCancel || !aHandled) {
    3169           0 :     return NS_ERROR_NULL_POINTER;
    3170             :   }
    3171           0 :   OwningNonNull<nsIAtom> listType = NS_Atomize(*aListType);
    3172             : 
    3173           0 :   WillInsert(*aSelection, aCancel);
    3174             : 
    3175             :   // initialize out param
    3176             :   // we want to ignore result of WillInsert()
    3177           0 :   *aCancel = false;
    3178           0 :   *aHandled = false;
    3179             : 
    3180             :   // deduce what tag to use for list items
    3181           0 :   nsCOMPtr<nsIAtom> itemType;
    3182           0 :   if (aItemType) {
    3183           0 :     itemType = NS_Atomize(*aItemType);
    3184           0 :     NS_ENSURE_TRUE(itemType, NS_ERROR_OUT_OF_MEMORY);
    3185           0 :   } else if (listType == nsGkAtoms::dl) {
    3186           0 :     itemType = nsGkAtoms::dd;
    3187             :   } else {
    3188           0 :     itemType = nsGkAtoms::li;
    3189             :   }
    3190             : 
    3191             :   // convert the selection ranges into "promoted" selection ranges:
    3192             :   // this basically just expands the range to include the immediate
    3193             :   // block parent, and then further expands to include any ancestors
    3194             :   // whose children are all in the range
    3195             : 
    3196           0 :   *aHandled = true;
    3197             : 
    3198           0 :   nsresult rv = NormalizeSelection(aSelection);
    3199           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3200           0 :   NS_ENSURE_STATE(mHTMLEditor);
    3201           0 :   AutoSelectionRestorer selectionRestorer(aSelection, mHTMLEditor);
    3202             : 
    3203           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
    3204           0 :   rv = GetListActionNodes(arrayOfNodes,
    3205           0 :                           aEntireList ? EntireList::yes : EntireList::no);
    3206           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3207             : 
    3208             :   // check if all our nodes are <br>s, or empty inlines
    3209           0 :   bool bOnlyBreaks = true;
    3210           0 :   for (auto& curNode : arrayOfNodes) {
    3211             :     // if curNode is not a Break or empty inline, we're done
    3212           0 :     if (!TextEditUtils::IsBreak(curNode) &&
    3213           0 :         !IsEmptyInline(curNode)) {
    3214           0 :       bOnlyBreaks = false;
    3215           0 :       break;
    3216             :     }
    3217             :   }
    3218             : 
    3219             :   // if no nodes, we make empty list.  Ditto if the user tried to make a list
    3220             :   // of some # of breaks.
    3221           0 :   if (arrayOfNodes.IsEmpty() || bOnlyBreaks) {
    3222             :     // if only breaks, delete them
    3223           0 :     if (bOnlyBreaks) {
    3224           0 :       for (auto& node : arrayOfNodes) {
    3225           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3226           0 :         rv = mHTMLEditor->DeleteNode(node);
    3227           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3228             :       }
    3229             :     }
    3230             : 
    3231             :     // get selection location
    3232           0 :     NS_ENSURE_STATE(aSelection->RangeCount());
    3233             :     nsCOMPtr<nsINode> container =
    3234           0 :       aSelection->GetRangeAt(0)->GetStartContainer();
    3235           0 :     int32_t offset = aSelection->GetRangeAt(0)->StartOffset();
    3236           0 :     NS_ENSURE_STATE(container);
    3237             : 
    3238             :     // make sure we can put a list here
    3239           0 :     NS_ENSURE_STATE(mHTMLEditor);
    3240           0 :     if (!mHTMLEditor->CanContainTag(*container, listType)) {
    3241           0 :       *aCancel = true;
    3242           0 :       return NS_OK;
    3243             :     }
    3244           0 :     rv = SplitAsNeeded(listType, container, offset);
    3245           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3246           0 :     NS_ENSURE_STATE(mHTMLEditor);
    3247             :     nsCOMPtr<Element> theList =
    3248           0 :       mHTMLEditor->CreateNode(listType, container, offset);
    3249           0 :     NS_ENSURE_STATE(theList);
    3250             : 
    3251           0 :     NS_ENSURE_STATE(mHTMLEditor);
    3252             :     nsCOMPtr<Element> theListItem =
    3253           0 :       mHTMLEditor->CreateNode(itemType, theList, 0);
    3254           0 :     NS_ENSURE_STATE(theListItem);
    3255             : 
    3256             :     // remember our new block for postprocessing
    3257           0 :     mNewBlock = theListItem;
    3258             :     // put selection in new list item
    3259           0 :     *aHandled = true;
    3260           0 :     rv = aSelection->Collapse(theListItem, 0);
    3261             :     // Don't restore the selection
    3262           0 :     selectionRestorer.Abort();
    3263           0 :     return rv;
    3264             :   }
    3265             : 
    3266             :   // if there is only one node in the array, and it is a list, div, or
    3267             :   // blockquote, then look inside of it until we find inner list or content.
    3268             : 
    3269           0 :   LookInsideDivBQandList(arrayOfNodes);
    3270             : 
    3271             :   // Ok, now go through all the nodes and put then in the list,
    3272             :   // or whatever is approriate.  Wohoo!
    3273             : 
    3274           0 :   uint32_t listCount = arrayOfNodes.Length();
    3275           0 :   nsCOMPtr<nsINode> curParent;
    3276           0 :   nsCOMPtr<Element> curList, prevListItem;
    3277             : 
    3278           0 :   for (uint32_t i = 0; i < listCount; i++) {
    3279             :     // here's where we actually figure out what to do
    3280           0 :     nsCOMPtr<Element> newBlock;
    3281           0 :     NS_ENSURE_STATE(arrayOfNodes[i]->IsContent());
    3282           0 :     OwningNonNull<nsIContent> curNode = *arrayOfNodes[i]->AsContent();
    3283             :     int32_t offset;
    3284           0 :     curParent = EditorBase::GetNodeLocation(curNode, &offset);
    3285             : 
    3286             :     // make sure we don't assemble content that is in different table cells
    3287             :     // into the same list.  respect table cell boundaries when listifying.
    3288           0 :     if (curList && InDifferentTableElements(curList, curNode)) {
    3289           0 :       curList = nullptr;
    3290             :     }
    3291             : 
    3292             :     // If curNode is a break, delete it, and quit remembering prev list item.
    3293             :     // If an empty inline container, delete it, but still remember the previous
    3294             :     // item.
    3295           0 :     NS_ENSURE_STATE(mHTMLEditor);
    3296           0 :     if (mHTMLEditor->IsEditable(curNode) && (TextEditUtils::IsBreak(curNode) ||
    3297           0 :                                              IsEmptyInline(curNode))) {
    3298           0 :       NS_ENSURE_STATE(mHTMLEditor);
    3299           0 :       rv = mHTMLEditor->DeleteNode(curNode);
    3300           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3301           0 :       if (TextEditUtils::IsBreak(curNode)) {
    3302           0 :         prevListItem = nullptr;
    3303             :       }
    3304           0 :       continue;
    3305             :     }
    3306             : 
    3307           0 :     if (HTMLEditUtils::IsList(curNode)) {
    3308             :       // do we have a curList already?
    3309           0 :       if (curList && !EditorUtils::IsDescendantOf(curNode, curList)) {
    3310             :         // move all of our children into curList.  cheezy way to do it: move
    3311             :         // whole list and then RemoveContainer() on the list.  ConvertListType
    3312             :         // first: that routine handles converting the list item types, if
    3313             :         // needed
    3314           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3315           0 :         rv = mHTMLEditor->MoveNode(curNode, curList, -1);
    3316           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3317           0 :         rv = ConvertListType(curNode->AsElement(), getter_AddRefs(newBlock),
    3318           0 :                              listType, itemType);
    3319           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3320           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3321           0 :         rv = mHTMLEditor->RemoveBlockContainer(*newBlock);
    3322           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3323             :       } else {
    3324             :         // replace list with new list type
    3325           0 :         rv = ConvertListType(curNode->AsElement(), getter_AddRefs(newBlock),
    3326           0 :                              listType, itemType);
    3327           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3328           0 :         curList = newBlock;
    3329             :       }
    3330           0 :       prevListItem = nullptr;
    3331           0 :       continue;
    3332             :     }
    3333             : 
    3334           0 :     if (HTMLEditUtils::IsListItem(curNode)) {
    3335           0 :       NS_ENSURE_STATE(mHTMLEditor);
    3336           0 :       if (!curParent->IsHTMLElement(listType)) {
    3337             :         // list item is in wrong type of list. if we don't have a curList,
    3338             :         // split the old list and make a new list of correct type.
    3339           0 :         if (!curList || EditorUtils::IsDescendantOf(curNode, curList)) {
    3340           0 :           NS_ENSURE_STATE(mHTMLEditor);
    3341           0 :           NS_ENSURE_STATE(curParent->IsContent());
    3342           0 :           ErrorResult rv;
    3343             :           nsCOMPtr<nsIContent> splitNode =
    3344           0 :             mHTMLEditor->SplitNode(*curParent->AsContent(), offset, rv);
    3345           0 :           NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
    3346           0 :           newBlock = splitNode ? splitNode->AsElement() : nullptr;
    3347             :           int32_t offset;
    3348             :           nsCOMPtr<nsINode> parent = EditorBase::GetNodeLocation(curParent,
    3349           0 :                                                                  &offset);
    3350           0 :           NS_ENSURE_STATE(mHTMLEditor);
    3351           0 :           curList = mHTMLEditor->CreateNode(listType, parent, offset);
    3352           0 :           NS_ENSURE_STATE(curList);
    3353             :         }
    3354             :         // move list item to new list
    3355           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3356           0 :         rv = mHTMLEditor->MoveNode(curNode, curList, -1);
    3357           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3358             :         // convert list item type if needed
    3359           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3360           0 :         if (!curNode->IsHTMLElement(itemType)) {
    3361           0 :           NS_ENSURE_STATE(mHTMLEditor);
    3362           0 :           newBlock = mHTMLEditor->ReplaceContainer(curNode->AsElement(),
    3363           0 :                                                    itemType);
    3364           0 :           NS_ENSURE_STATE(newBlock);
    3365             :         }
    3366             :       } else {
    3367             :         // item is in right type of list.  But we might still have to move it.
    3368             :         // and we might need to convert list item types.
    3369           0 :         if (!curList) {
    3370           0 :           curList = curParent->AsElement();
    3371           0 :         } else if (curParent != curList) {
    3372             :           // move list item to new list
    3373           0 :           NS_ENSURE_STATE(mHTMLEditor);
    3374           0 :           rv = mHTMLEditor->MoveNode(curNode, curList, -1);
    3375           0 :           NS_ENSURE_SUCCESS(rv, rv);
    3376             :         }
    3377           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3378           0 :         if (!curNode->IsHTMLElement(itemType)) {
    3379           0 :           NS_ENSURE_STATE(mHTMLEditor);
    3380           0 :           newBlock = mHTMLEditor->ReplaceContainer(curNode->AsElement(),
    3381           0 :                                                    itemType);
    3382           0 :           NS_ENSURE_STATE(newBlock);
    3383             :         }
    3384             :       }
    3385           0 :       NS_ENSURE_STATE(mHTMLEditor);
    3386           0 :       nsCOMPtr<Element> curElement = do_QueryInterface(curNode);
    3387           0 :       if (aBulletType && !aBulletType->IsEmpty()) {
    3388           0 :         rv = mHTMLEditor->SetAttribute(curElement, nsGkAtoms::type,
    3389           0 :                                        *aBulletType);
    3390           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    3391           0 :           return rv;
    3392             :         }
    3393             :       } else {
    3394           0 :         rv = mHTMLEditor->RemoveAttribute(curElement, nsGkAtoms::type);
    3395           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    3396           0 :           return rv;
    3397             :         }
    3398             :       }
    3399           0 :       continue;
    3400             :     }
    3401             : 
    3402             :     // if we hit a div clear our prevListItem, insert divs contents
    3403             :     // into our node array, and remove the div
    3404           0 :     if (curNode->IsHTMLElement(nsGkAtoms::div)) {
    3405           0 :       prevListItem = nullptr;
    3406           0 :       int32_t j = i + 1;
    3407           0 :       GetInnerContent(*curNode, arrayOfNodes, &j);
    3408           0 :       NS_ENSURE_STATE(mHTMLEditor);
    3409           0 :       rv = mHTMLEditor->RemoveContainer(curNode);
    3410           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3411           0 :       listCount = arrayOfNodes.Length();
    3412           0 :       continue;
    3413             :     }
    3414             : 
    3415             :     // need to make a list to put things in if we haven't already,
    3416           0 :     if (!curList) {
    3417           0 :       rv = SplitAsNeeded(listType, curParent, offset);
    3418           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3419           0 :       NS_ENSURE_STATE(mHTMLEditor);
    3420           0 :       curList = mHTMLEditor->CreateNode(listType, curParent, offset);
    3421             :       // remember our new block for postprocessing
    3422           0 :       mNewBlock = curList;
    3423             :       // curList is now the correct thing to put curNode in
    3424           0 :       prevListItem = nullptr;
    3425             :     }
    3426             : 
    3427             :     // if curNode isn't a list item, we must wrap it in one
    3428           0 :     nsCOMPtr<Element> listItem;
    3429           0 :     if (!HTMLEditUtils::IsListItem(curNode)) {
    3430           0 :       if (IsInlineNode(curNode) && prevListItem) {
    3431             :         // this is a continuation of some inline nodes that belong together in
    3432             :         // the same list item.  use prevListItem
    3433           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3434           0 :         rv = mHTMLEditor->MoveNode(curNode, prevListItem, -1);
    3435           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3436             :       } else {
    3437             :         // don't wrap li around a paragraph.  instead replace paragraph with li
    3438           0 :         if (curNode->IsHTMLElement(nsGkAtoms::p)) {
    3439           0 :           NS_ENSURE_STATE(mHTMLEditor);
    3440           0 :           listItem = mHTMLEditor->ReplaceContainer(curNode->AsElement(),
    3441           0 :                                                    itemType);
    3442           0 :           NS_ENSURE_STATE(listItem);
    3443             :         } else {
    3444           0 :           NS_ENSURE_STATE(mHTMLEditor);
    3445           0 :           listItem = mHTMLEditor->InsertContainerAbove(curNode, itemType);
    3446           0 :           NS_ENSURE_STATE(listItem);
    3447             :         }
    3448           0 :         if (IsInlineNode(curNode)) {
    3449           0 :           prevListItem = listItem;
    3450             :         } else {
    3451           0 :           prevListItem = nullptr;
    3452             :         }
    3453             :       }
    3454             :     } else {
    3455           0 :       listItem = curNode->AsElement();
    3456             :     }
    3457             : 
    3458           0 :     if (listItem) {
    3459             :       // if we made a new list item, deal with it: tuck the listItem into the
    3460             :       // end of the active list
    3461           0 :       NS_ENSURE_STATE(mHTMLEditor);
    3462           0 :       rv = mHTMLEditor->MoveNode(listItem, curList, -1);
    3463           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3464             :     }
    3465             :   }
    3466             : 
    3467           0 :   return NS_OK;
    3468             : }
    3469             : 
    3470             : 
    3471             : nsresult
    3472           0 : HTMLEditRules::WillRemoveList(Selection* aSelection,
    3473             :                               bool aOrdered,
    3474             :                               bool* aCancel,
    3475             :                               bool* aHandled)
    3476             : {
    3477           0 :   if (!aSelection || !aCancel || !aHandled) {
    3478           0 :     return NS_ERROR_NULL_POINTER;
    3479             :   }
    3480             :   // initialize out param
    3481           0 :   *aCancel = false;
    3482           0 :   *aHandled = true;
    3483             : 
    3484           0 :   nsresult rv = NormalizeSelection(aSelection);
    3485           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3486           0 :   NS_ENSURE_STATE(mHTMLEditor);
    3487           0 :   AutoSelectionRestorer selectionRestorer(aSelection, mHTMLEditor);
    3488             : 
    3489           0 :   nsTArray<RefPtr<nsRange>> arrayOfRanges;
    3490           0 :   GetPromotedRanges(*aSelection, arrayOfRanges, EditAction::makeList);
    3491             : 
    3492             :   // use these ranges to contruct a list of nodes to act on.
    3493           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
    3494           0 :   rv = GetListActionNodes(arrayOfNodes, EntireList::no);
    3495           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3496             : 
    3497             :   // Remove all non-editable nodes.  Leave them be.
    3498           0 :   for (int32_t i = arrayOfNodes.Length() - 1; i >= 0; i--) {
    3499           0 :     OwningNonNull<nsINode> testNode = arrayOfNodes[i];
    3500           0 :     NS_ENSURE_STATE(mHTMLEditor);
    3501           0 :     if (!mHTMLEditor->IsEditable(testNode)) {
    3502           0 :       arrayOfNodes.RemoveElementAt(i);
    3503             :     }
    3504             :   }
    3505             : 
    3506             :   // Only act on lists or list items in the array
    3507           0 :   for (auto& curNode : arrayOfNodes) {
    3508             :     // here's where we actually figure out what to do
    3509           0 :     if (HTMLEditUtils::IsListItem(curNode)) {
    3510             :       // unlist this listitem
    3511             :       bool bOutOfList;
    3512           0 :       do {
    3513           0 :         rv = PopListItem(*curNode->AsContent(), &bOutOfList);
    3514           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3515           0 :       } while (!bOutOfList); // keep popping it out until it's not in a list anymore
    3516           0 :     } else if (HTMLEditUtils::IsList(curNode)) {
    3517             :       // node is a list, move list items out
    3518           0 :       rv = RemoveListStructure(*curNode->AsElement());
    3519           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3520             :     }
    3521             :   }
    3522           0 :   return NS_OK;
    3523             : }
    3524             : 
    3525             : nsresult
    3526           0 : HTMLEditRules::WillMakeDefListItem(Selection* aSelection,
    3527             :                                    const nsAString *aItemType,
    3528             :                                    bool aEntireList,
    3529             :                                    bool* aCancel,
    3530             :                                    bool* aHandled)
    3531             : {
    3532             :   // for now we let WillMakeList handle this
    3533           0 :   NS_NAMED_LITERAL_STRING(listType, "dl");
    3534           0 :   return WillMakeList(aSelection, &listType.AsString(), aEntireList, nullptr,
    3535           0 :                       aCancel, aHandled, aItemType);
    3536             : }
    3537             : 
    3538             : nsresult
    3539           0 : HTMLEditRules::WillMakeBasicBlock(Selection& aSelection,
    3540             :                                   const nsAString& aBlockType,
    3541             :                                   bool* aCancel,
    3542             :                                   bool* aHandled)
    3543             : {
    3544           0 :   MOZ_ASSERT(aCancel && aHandled);
    3545             : 
    3546           0 :   OwningNonNull<nsIAtom> blockType = NS_Atomize(aBlockType);
    3547             : 
    3548           0 :   WillInsert(aSelection, aCancel);
    3549             :   // We want to ignore result of WillInsert()
    3550           0 :   *aCancel = false;
    3551           0 :   *aHandled = true;
    3552             : 
    3553           0 :   nsresult rv = MakeBasicBlock(aSelection, blockType);
    3554           0 :   Unused << NS_WARN_IF(NS_FAILED(rv));
    3555           0 :   return rv;
    3556             : }
    3557             : 
    3558             : nsresult
    3559           0 : HTMLEditRules::MakeBasicBlock(Selection& aSelection, nsIAtom& blockType)
    3560             : {
    3561           0 :   NS_ENSURE_STATE(mHTMLEditor);
    3562           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    3563             : 
    3564           0 :   nsresult rv = NormalizeSelection(&aSelection);
    3565           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3566           0 :   AutoSelectionRestorer selectionRestorer(&aSelection, htmlEditor);
    3567           0 :   AutoTransactionsConserveSelection dontSpazMySelection(htmlEditor);
    3568             : 
    3569             :   // Contruct a list of nodes to act on.
    3570           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
    3571             :   rv = GetNodesFromSelection(aSelection, EditAction::makeBasicBlock,
    3572           0 :                              arrayOfNodes);
    3573           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3574             : 
    3575             :   // If nothing visible in list, make an empty block
    3576           0 :   if (ListIsEmptyLine(arrayOfNodes)) {
    3577             :     // Get selection location
    3578           0 :     NS_ENSURE_STATE(aSelection.GetRangeAt(0) &&
    3579             :                     aSelection.GetRangeAt(0)->GetStartContainer());
    3580             :     OwningNonNull<nsINode> container =
    3581           0 :       *aSelection.GetRangeAt(0)->GetStartContainer();
    3582           0 :     int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
    3583             : 
    3584           0 :     if (&blockType == nsGkAtoms::normal ||
    3585           0 :         &blockType == nsGkAtoms::_empty) {
    3586             :       // We are removing blocks (going to "body text")
    3587           0 :       NS_ENSURE_TRUE(htmlEditor->GetBlock(container), NS_ERROR_NULL_POINTER);
    3588           0 :       OwningNonNull<Element> curBlock = *htmlEditor->GetBlock(container);
    3589           0 :       if (HTMLEditUtils::IsFormatNode(curBlock)) {
    3590             :         // If the first editable node after selection is a br, consume it.
    3591             :         // Otherwise it gets pushed into a following block after the split,
    3592             :         // which is visually bad.
    3593             :         nsCOMPtr<nsIContent> brNode =
    3594           0 :           htmlEditor->GetNextHTMLNode(container, offset);
    3595           0 :         if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
    3596           0 :           rv = htmlEditor->DeleteNode(brNode);
    3597           0 :           NS_ENSURE_SUCCESS(rv, rv);
    3598             :         }
    3599             :         // Do the splits!
    3600           0 :         offset = htmlEditor->SplitNodeDeep(curBlock, *container->AsContent(),
    3601             :                                             offset,
    3602           0 :                                             HTMLEditor::EmptyContainers::no);
    3603           0 :         NS_ENSURE_STATE(offset != -1);
    3604             :         // Put a br at the split point
    3605           0 :         brNode = htmlEditor->CreateBR(curBlock->GetParentNode(), offset);
    3606           0 :         NS_ENSURE_STATE(brNode);
    3607             :         // Put selection at the split point
    3608           0 :         rv = aSelection.Collapse(curBlock->GetParentNode(), offset);
    3609             :         // Don't restore the selection
    3610           0 :         selectionRestorer.Abort();
    3611           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3612           0 :       }
    3613             :       // Else nothing to do!
    3614             :     } else {
    3615             :       // We are making a block.  Consume a br, if needed.
    3616             :       nsCOMPtr<nsIContent> brNode =
    3617           0 :         htmlEditor->GetNextHTMLNode(container, offset, true);
    3618           0 :       if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
    3619           0 :         rv = htmlEditor->DeleteNode(brNode);
    3620           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3621             :         // We don't need to act on this node any more
    3622           0 :         arrayOfNodes.RemoveElement(brNode);
    3623             :       }
    3624             :       // Make sure we can put a block here
    3625           0 :       rv = SplitAsNeeded(blockType, container, offset);
    3626           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3627             :       nsCOMPtr<Element> block =
    3628           0 :         htmlEditor->CreateNode(&blockType, container, offset);
    3629           0 :       NS_ENSURE_STATE(block);
    3630             :       // Remember our new block for postprocessing
    3631           0 :       mNewBlock = block;
    3632             :       // Delete anything that was in the list of nodes
    3633           0 :       while (!arrayOfNodes.IsEmpty()) {
    3634           0 :         OwningNonNull<nsINode> curNode = arrayOfNodes[0];
    3635           0 :         rv = htmlEditor->DeleteNode(curNode);
    3636           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3637           0 :         arrayOfNodes.RemoveElementAt(0);
    3638             :       }
    3639             :       // Put selection in new block
    3640           0 :       rv = aSelection.Collapse(block, 0);
    3641             :       // Don't restore the selection
    3642           0 :       selectionRestorer.Abort();
    3643           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3644             :     }
    3645           0 :     return NS_OK;
    3646             :   }
    3647             :   // Okay, now go through all the nodes and make the right kind of blocks, or
    3648             :   // whatever is approriate.  Woohoo!  Note: blockquote is handled a little
    3649             :   // differently.
    3650           0 :   if (&blockType == nsGkAtoms::blockquote) {
    3651           0 :     rv = MakeBlockquote(arrayOfNodes);
    3652           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3653           0 :   } else if (&blockType == nsGkAtoms::normal ||
    3654           0 :              &blockType == nsGkAtoms::_empty) {
    3655           0 :     rv = RemoveBlockStyle(arrayOfNodes);
    3656           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3657             :   } else {
    3658           0 :     rv = ApplyBlockStyle(arrayOfNodes, blockType);
    3659           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3660             :   }
    3661           0 :   return NS_OK;
    3662             : }
    3663             : 
    3664             : nsresult
    3665           0 : HTMLEditRules::DidMakeBasicBlock(Selection* aSelection,
    3666             :                                  RulesInfo* aInfo,
    3667             :                                  nsresult aResult)
    3668             : {
    3669           0 :   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
    3670             :   // check for empty block.  if so, put a moz br in it.
    3671           0 :   if (!aSelection->Collapsed()) {
    3672           0 :     return NS_OK;
    3673             :   }
    3674             : 
    3675           0 :   NS_ENSURE_STATE(aSelection->GetRangeAt(0) &&
    3676             :                   aSelection->GetRangeAt(0)->GetStartContainer());
    3677             :   nsresult rv =
    3678           0 :     InsertMozBRIfNeeded(*aSelection->GetRangeAt(0)->GetStartContainer());
    3679           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3680           0 :   return NS_OK;
    3681             : }
    3682             : 
    3683             : nsresult
    3684           0 : HTMLEditRules::WillIndent(Selection* aSelection,
    3685             :                           bool* aCancel,
    3686             :                           bool* aHandled)
    3687             : {
    3688           0 :   NS_ENSURE_STATE(mHTMLEditor);
    3689           0 :   if (mHTMLEditor->IsCSSEnabled()) {
    3690           0 :     nsresult rv = WillCSSIndent(aSelection, aCancel, aHandled);
    3691           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    3692           0 :       return rv;
    3693             :     }
    3694             :   } else {
    3695           0 :     nsresult rv = WillHTMLIndent(aSelection, aCancel, aHandled);
    3696           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    3697           0 :       return rv;
    3698             :     }
    3699             :   }
    3700           0 :   return NS_OK;
    3701             : }
    3702             : 
    3703             : nsresult
    3704           0 : HTMLEditRules::WillCSSIndent(Selection* aSelection,
    3705             :                              bool* aCancel,
    3706             :                              bool* aHandled)
    3707             : {
    3708           0 :   if (!aSelection || !aCancel || !aHandled) {
    3709           0 :     return NS_ERROR_NULL_POINTER;
    3710             :   }
    3711             : 
    3712           0 :   WillInsert(*aSelection, aCancel);
    3713             : 
    3714             :   // initialize out param
    3715             :   // we want to ignore result of WillInsert()
    3716           0 :   *aCancel = false;
    3717           0 :   *aHandled = true;
    3718             : 
    3719           0 :   nsresult rv = NormalizeSelection(aSelection);
    3720           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3721           0 :   NS_ENSURE_STATE(mHTMLEditor);
    3722           0 :   AutoSelectionRestorer selectionRestorer(aSelection, mHTMLEditor);
    3723           0 :   nsTArray<OwningNonNull<nsRange>> arrayOfRanges;
    3724           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
    3725             : 
    3726             :   // short circuit: detect case of collapsed selection inside an <li>.
    3727             :   // just sublist that <li>.  This prevents bug 97797.
    3728             : 
    3729           0 :   nsCOMPtr<Element> liNode;
    3730           0 :   if (aSelection->Collapsed()) {
    3731           0 :     nsCOMPtr<nsINode> node;
    3732             :     int32_t offset;
    3733             :     nsresult rv =
    3734           0 :       EditorBase::GetStartNodeAndOffset(aSelection,
    3735           0 :                                         getter_AddRefs(node), &offset);
    3736           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3737           0 :     NS_ENSURE_STATE(mHTMLEditor);
    3738           0 :     nsCOMPtr<Element> block = mHTMLEditor->GetBlock(*node);
    3739           0 :     if (block && HTMLEditUtils::IsListItem(block)) {
    3740           0 :       liNode = block;
    3741             :     }
    3742             :   }
    3743             : 
    3744           0 :   if (liNode) {
    3745           0 :     arrayOfNodes.AppendElement(*liNode);
    3746             :   } else {
    3747             :     // convert the selection ranges into "promoted" selection ranges:
    3748             :     // this basically just expands the range to include the immediate
    3749             :     // block parent, and then further expands to include any ancestors
    3750             :     // whose children are all in the range
    3751           0 :     rv = GetNodesFromSelection(*aSelection, EditAction::indent, arrayOfNodes);
    3752           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3753             :   }
    3754             : 
    3755             :   // if nothing visible in list, make an empty block
    3756           0 :   if (ListIsEmptyLine(arrayOfNodes)) {
    3757             :     // get selection location
    3758           0 :     NS_ENSURE_STATE(aSelection->RangeCount());
    3759           0 :     nsCOMPtr<nsINode> container = aSelection->GetRangeAt(0)->GetStartContainer();
    3760           0 :     int32_t offset = aSelection->GetRangeAt(0)->StartOffset();
    3761           0 :     NS_ENSURE_STATE(container);
    3762             : 
    3763             :     // make sure we can put a block here
    3764           0 :     rv = SplitAsNeeded(*nsGkAtoms::div, container, offset);
    3765           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3766           0 :     NS_ENSURE_STATE(mHTMLEditor);
    3767           0 :     nsCOMPtr<Element> theBlock = mHTMLEditor->CreateNode(nsGkAtoms::div,
    3768           0 :                                                          container, offset);
    3769           0 :     NS_ENSURE_STATE(theBlock);
    3770             :     // remember our new block for postprocessing
    3771           0 :     mNewBlock = theBlock;
    3772           0 :     ChangeIndentation(*theBlock, Change::plus);
    3773             :     // delete anything that was in the list of nodes
    3774           0 :     while (!arrayOfNodes.IsEmpty()) {
    3775           0 :       OwningNonNull<nsINode> curNode = arrayOfNodes[0];
    3776           0 :       NS_ENSURE_STATE(mHTMLEditor);
    3777           0 :       rv = mHTMLEditor->DeleteNode(curNode);
    3778           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3779           0 :       arrayOfNodes.RemoveElementAt(0);
    3780             :     }
    3781             :     // put selection in new block
    3782           0 :     *aHandled = true;
    3783           0 :     rv = aSelection->Collapse(theBlock, 0);
    3784             :     // Don't restore the selection
    3785           0 :     selectionRestorer.Abort();
    3786           0 :     return rv;
    3787             :   }
    3788             : 
    3789             :   // Ok, now go through all the nodes and put them in a blockquote,
    3790             :   // or whatever is appropriate.  Wohoo!
    3791           0 :   nsCOMPtr<nsINode> curParent;
    3792           0 :   nsCOMPtr<Element> curList, curQuote;
    3793           0 :   nsCOMPtr<nsIContent> sibling;
    3794           0 :   int32_t listCount = arrayOfNodes.Length();
    3795           0 :   for (int32_t i = 0; i < listCount; i++) {
    3796             :     // here's where we actually figure out what to do
    3797           0 :     NS_ENSURE_STATE(arrayOfNodes[i]->IsContent());
    3798           0 :     nsCOMPtr<nsIContent> curNode = arrayOfNodes[i]->AsContent();
    3799             : 
    3800             :     // Ignore all non-editable nodes.  Leave them be.
    3801           0 :     NS_ENSURE_STATE(mHTMLEditor);
    3802           0 :     if (!mHTMLEditor->IsEditable(curNode)) {
    3803           0 :       continue;
    3804             :     }
    3805             : 
    3806           0 :     curParent = curNode->GetParentNode();
    3807           0 :     int32_t offset = curParent ? curParent->IndexOf(curNode) : -1;
    3808             : 
    3809             :     // some logic for putting list items into nested lists...
    3810           0 :     if (HTMLEditUtils::IsList(curParent)) {
    3811           0 :       sibling = nullptr;
    3812             : 
    3813             :       // Check for whether we should join a list that follows curNode.
    3814             :       // We do this if the next element is a list, and the list is of the
    3815             :       // same type (li/ol) as curNode was a part it.
    3816           0 :       NS_ENSURE_STATE(mHTMLEditor);
    3817           0 :       sibling = mHTMLEditor->GetNextHTMLSibling(curNode);
    3818           0 :       if (sibling && HTMLEditUtils::IsList(sibling) &&
    3819           0 :           curParent->NodeInfo()->NameAtom() ==
    3820           0 :             sibling->NodeInfo()->NameAtom() &&
    3821           0 :           curParent->NodeInfo()->NamespaceID() ==
    3822           0 :             sibling->NodeInfo()->NamespaceID()) {
    3823           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3824           0 :         rv = mHTMLEditor->MoveNode(curNode, sibling, 0);
    3825           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3826           0 :         continue;
    3827             :       }
    3828             :       // Check for whether we should join a list that preceeds curNode.
    3829             :       // We do this if the previous element is a list, and the list is of
    3830             :       // the same type (li/ol) as curNode was a part of.
    3831           0 :       NS_ENSURE_STATE(mHTMLEditor);
    3832           0 :       sibling = mHTMLEditor->GetPriorHTMLSibling(curNode);
    3833           0 :       if (sibling && HTMLEditUtils::IsList(sibling) &&
    3834           0 :           curParent->NodeInfo()->NameAtom() ==
    3835           0 :             sibling->NodeInfo()->NameAtom() &&
    3836           0 :           curParent->NodeInfo()->NamespaceID() ==
    3837           0 :             sibling->NodeInfo()->NamespaceID()) {
    3838           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3839           0 :         rv = mHTMLEditor->MoveNode(curNode, sibling, -1);
    3840           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3841           0 :         continue;
    3842             :       }
    3843           0 :       sibling = nullptr;
    3844             : 
    3845             :       // check to see if curList is still appropriate.  Which it is if
    3846             :       // curNode is still right after it in the same list.
    3847           0 :       if (curList) {
    3848           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3849           0 :         sibling = mHTMLEditor->GetPriorHTMLSibling(curNode);
    3850             :       }
    3851             : 
    3852           0 :       if (!curList || (sibling && sibling != curList)) {
    3853             :         // create a new nested list of correct type
    3854             :         rv =
    3855           0 :           SplitAsNeeded(*curParent->NodeInfo()->NameAtom(), curParent, offset);
    3856           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3857           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3858           0 :         curList = mHTMLEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
    3859           0 :                                           curParent, offset);
    3860           0 :         NS_ENSURE_STATE(curList);
    3861             :         // curList is now the correct thing to put curNode in
    3862             :         // remember our new block for postprocessing
    3863           0 :         mNewBlock = curList;
    3864             :       }
    3865             :       // tuck the node into the end of the active list
    3866           0 :       uint32_t listLen = curList->Length();
    3867           0 :       NS_ENSURE_STATE(mHTMLEditor);
    3868           0 :       rv = mHTMLEditor->MoveNode(curNode, curList, listLen);
    3869           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3870             :     }
    3871             :     // Not a list item.
    3872             :     else {
    3873           0 :       if (curNode && IsBlockNode(*curNode)) {
    3874           0 :         ChangeIndentation(*curNode->AsElement(), Change::plus);
    3875           0 :         curQuote = nullptr;
    3876             :       } else {
    3877           0 :         if (!curQuote) {
    3878             :           // First, check that our element can contain a div.
    3879           0 :           if (NS_WARN_IF(!mHTMLEditor)) {
    3880           0 :             return NS_ERROR_UNEXPECTED;
    3881             :           }
    3882           0 :           if (!mHTMLEditor->CanContainTag(*curParent, *nsGkAtoms::div)) {
    3883           0 :             return NS_OK; // cancelled
    3884             :           }
    3885             : 
    3886           0 :           rv = SplitAsNeeded(*nsGkAtoms::div, curParent, offset);
    3887           0 :           NS_ENSURE_SUCCESS(rv, rv);
    3888           0 :           NS_ENSURE_STATE(mHTMLEditor);
    3889           0 :           curQuote = mHTMLEditor->CreateNode(nsGkAtoms::div, curParent,
    3890           0 :                                              offset);
    3891           0 :           NS_ENSURE_STATE(curQuote);
    3892           0 :           ChangeIndentation(*curQuote, Change::plus);
    3893             :           // remember our new block for postprocessing
    3894           0 :           mNewBlock = curQuote;
    3895             :           // curQuote is now the correct thing to put curNode in
    3896             :         }
    3897             : 
    3898             :         // tuck the node into the end of the active blockquote
    3899           0 :         uint32_t quoteLen = curQuote->Length();
    3900           0 :         NS_ENSURE_STATE(mHTMLEditor);
    3901           0 :         rv = mHTMLEditor->MoveNode(curNode, curQuote, quoteLen);
    3902           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3903             :       }
    3904             :     }
    3905             :   }
    3906           0 :   return NS_OK;
    3907             : }
    3908             : 
    3909             : nsresult
    3910           0 : HTMLEditRules::WillHTMLIndent(Selection* aSelection,
    3911             :                               bool* aCancel,
    3912             :                               bool* aHandled)
    3913             : {
    3914           0 :   if (!aSelection || !aCancel || !aHandled) {
    3915           0 :     return NS_ERROR_NULL_POINTER;
    3916             :   }
    3917           0 :   WillInsert(*aSelection, aCancel);
    3918             : 
    3919             :   // initialize out param
    3920             :   // we want to ignore result of WillInsert()
    3921           0 :   *aCancel = false;
    3922           0 :   *aHandled = true;
    3923             : 
    3924           0 :   nsresult rv = NormalizeSelection(aSelection);
    3925           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3926           0 :   NS_ENSURE_STATE(mHTMLEditor);
    3927           0 :   AutoSelectionRestorer selectionRestorer(aSelection, mHTMLEditor);
    3928             : 
    3929             :   // convert the selection ranges into "promoted" selection ranges:
    3930             :   // this basically just expands the range to include the immediate
    3931             :   // block parent, and then further expands to include any ancestors
    3932             :   // whose children are all in the range
    3933             : 
    3934           0 :   nsTArray<RefPtr<nsRange>> arrayOfRanges;
    3935           0 :   GetPromotedRanges(*aSelection, arrayOfRanges, EditAction::indent);
    3936             : 
    3937             :   // use these ranges to contruct a list of nodes to act on.
    3938           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
    3939           0 :   rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes, EditAction::indent);
    3940           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3941             : 
    3942             :   // if nothing visible in list, make an empty block
    3943           0 :   if (ListIsEmptyLine(arrayOfNodes)) {
    3944             :     // get selection location
    3945           0 :     NS_ENSURE_STATE(aSelection->RangeCount());
    3946             :     nsCOMPtr<nsINode> container =
    3947           0 :       aSelection->GetRangeAt(0)->GetStartContainer();
    3948           0 :     int32_t offset = aSelection->GetRangeAt(0)->StartOffset();
    3949           0 :     NS_ENSURE_STATE(container);
    3950             : 
    3951             :     // make sure we can put a block here
    3952           0 :     rv = SplitAsNeeded(*nsGkAtoms::blockquote, container, offset);
    3953           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3954           0 :     NS_ENSURE_STATE(mHTMLEditor);
    3955           0 :     nsCOMPtr<Element> theBlock = mHTMLEditor->CreateNode(nsGkAtoms::blockquote,
    3956           0 :                                                          container, offset);
    3957           0 :     NS_ENSURE_STATE(theBlock);
    3958             :     // remember our new block for postprocessing
    3959           0 :     mNewBlock = theBlock;
    3960             :     // delete anything that was in the list of nodes
    3961           0 :     while (!arrayOfNodes.IsEmpty()) {
    3962           0 :       OwningNonNull<nsINode> curNode = arrayOfNodes[0];
    3963           0 :       NS_ENSURE_STATE(mHTMLEditor);
    3964           0 :       rv = mHTMLEditor->DeleteNode(curNode);
    3965           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3966           0 :       arrayOfNodes.RemoveElementAt(0);
    3967             :     }
    3968             :     // put selection in new block
    3969           0 :     *aHandled = true;
    3970           0 :     rv = aSelection->Collapse(theBlock, 0);
    3971             :     // Don't restore the selection
    3972           0 :     selectionRestorer.Abort();
    3973           0 :     return rv;
    3974             :   }
    3975             : 
    3976             :   // Ok, now go through all the nodes and put them in a blockquote,
    3977             :   // or whatever is appropriate.  Wohoo!
    3978           0 :   nsCOMPtr<nsINode> curParent;
    3979           0 :   nsCOMPtr<nsIContent> sibling;
    3980           0 :   nsCOMPtr<Element> curList, curQuote, indentedLI;
    3981           0 :   int32_t listCount = arrayOfNodes.Length();
    3982           0 :   for (int32_t i = 0; i < listCount; i++) {
    3983             :     // here's where we actually figure out what to do
    3984           0 :     NS_ENSURE_STATE(arrayOfNodes[i]->IsContent());
    3985           0 :     nsCOMPtr<nsIContent> curNode = arrayOfNodes[i]->AsContent();
    3986             : 
    3987             :     // Ignore all non-editable nodes.  Leave them be.
    3988           0 :     NS_ENSURE_STATE(mHTMLEditor);
    3989           0 :     if (!mHTMLEditor->IsEditable(curNode)) {
    3990           0 :       continue;
    3991             :     }
    3992             : 
    3993           0 :     curParent = curNode->GetParentNode();
    3994           0 :     int32_t offset = curParent ? curParent->IndexOf(curNode) : -1;
    3995             : 
    3996             :     // some logic for putting list items into nested lists...
    3997           0 :     if (HTMLEditUtils::IsList(curParent)) {
    3998           0 :       sibling = nullptr;
    3999             : 
    4000             :       // Check for whether we should join a list that follows curNode.
    4001             :       // We do this if the next element is a list, and the list is of the
    4002             :       // same type (li/ol) as curNode was a part it.
    4003           0 :       NS_ENSURE_STATE(mHTMLEditor);
    4004           0 :       sibling = mHTMLEditor->GetNextHTMLSibling(curNode);
    4005           0 :       if (sibling && HTMLEditUtils::IsList(sibling) &&
    4006           0 :           curParent->NodeInfo()->NameAtom() ==
    4007           0 :             sibling->NodeInfo()->NameAtom() &&
    4008           0 :           curParent->NodeInfo()->NamespaceID() ==
    4009           0 :             sibling->NodeInfo()->NamespaceID()) {
    4010           0 :         NS_ENSURE_STATE(mHTMLEditor);
    4011           0 :         rv = mHTMLEditor->MoveNode(curNode, sibling, 0);
    4012           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4013           0 :         continue;
    4014             :       }
    4015             : 
    4016             :       // Check for whether we should join a list that preceeds curNode.
    4017             :       // We do this if the previous element is a list, and the list is of
    4018             :       // the same type (li/ol) as curNode was a part of.
    4019           0 :       NS_ENSURE_STATE(mHTMLEditor);
    4020           0 :       sibling = mHTMLEditor->GetPriorHTMLSibling(curNode);
    4021           0 :       if (sibling && HTMLEditUtils::IsList(sibling) &&
    4022           0 :           curParent->NodeInfo()->NameAtom() ==
    4023           0 :             sibling->NodeInfo()->NameAtom() &&
    4024           0 :           curParent->NodeInfo()->NamespaceID() ==
    4025           0 :             sibling->NodeInfo()->NamespaceID()) {
    4026           0 :         NS_ENSURE_STATE(mHTMLEditor);
    4027           0 :         rv = mHTMLEditor->MoveNode(curNode, sibling, -1);
    4028           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4029           0 :         continue;
    4030             :       }
    4031             : 
    4032           0 :       sibling = nullptr;
    4033             : 
    4034             :       // check to see if curList is still appropriate.  Which it is if
    4035             :       // curNode is still right after it in the same list.
    4036           0 :       if (curList) {
    4037           0 :         NS_ENSURE_STATE(mHTMLEditor);
    4038           0 :         sibling = mHTMLEditor->GetPriorHTMLSibling(curNode);
    4039             :       }
    4040             : 
    4041           0 :       if (!curList || (sibling && sibling != curList)) {
    4042             :         // create a new nested list of correct type
    4043             :         rv =
    4044           0 :           SplitAsNeeded(*curParent->NodeInfo()->NameAtom(), curParent, offset);
    4045           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4046           0 :         NS_ENSURE_STATE(mHTMLEditor);
    4047           0 :         curList = mHTMLEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
    4048           0 :                                           curParent, offset);
    4049           0 :         NS_ENSURE_STATE(curList);
    4050             :         // curList is now the correct thing to put curNode in
    4051             :         // remember our new block for postprocessing
    4052           0 :         mNewBlock = curList;
    4053             :       }
    4054             :       // tuck the node into the end of the active list
    4055           0 :       NS_ENSURE_STATE(mHTMLEditor);
    4056           0 :       rv = mHTMLEditor->MoveNode(curNode, curList, -1);
    4057           0 :       NS_ENSURE_SUCCESS(rv, rv);
    4058             :       // forget curQuote, if any
    4059           0 :       curQuote = nullptr;
    4060             :     }
    4061             :     // Not a list item, use blockquote?
    4062             :     else {
    4063             :       // if we are inside a list item, we don't want to blockquote, we want
    4064             :       // to sublist the list item.  We may have several nodes listed in the
    4065             :       // array of nodes to act on, that are in the same list item.  Since
    4066             :       // we only want to indent that li once, we must keep track of the most
    4067             :       // recent indented list item, and not indent it if we find another node
    4068             :       // to act on that is still inside the same li.
    4069           0 :       nsCOMPtr<Element> listItem = IsInListItem(curNode);
    4070           0 :       if (listItem) {
    4071           0 :         if (indentedLI == listItem) {
    4072             :           // already indented this list item
    4073           0 :           continue;
    4074             :         }
    4075           0 :         curParent = listItem->GetParentNode();
    4076           0 :         offset = curParent ? curParent->IndexOf(listItem) : -1;
    4077             :         // check to see if curList is still appropriate.  Which it is if
    4078             :         // curNode is still right after it in the same list.
    4079           0 :         if (curList) {
    4080           0 :           sibling = nullptr;
    4081           0 :           NS_ENSURE_STATE(mHTMLEditor);
    4082           0 :           sibling = mHTMLEditor->GetPriorHTMLSibling(curNode);
    4083             :         }
    4084             : 
    4085           0 :         if (!curList || (sibling && sibling != curList)) {
    4086             :           // create a new nested list of correct type
    4087           0 :           rv = SplitAsNeeded(*curParent->NodeInfo()->NameAtom(), curParent,
    4088           0 :                              offset);
    4089           0 :           NS_ENSURE_SUCCESS(rv, rv);
    4090           0 :           NS_ENSURE_STATE(mHTMLEditor);
    4091           0 :           curList = mHTMLEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
    4092           0 :                                             curParent, offset);
    4093           0 :           NS_ENSURE_STATE(curList);
    4094             :         }
    4095           0 :         NS_ENSURE_STATE(mHTMLEditor);
    4096           0 :         rv = mHTMLEditor->MoveNode(listItem, curList, -1);
    4097           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4098             :         // remember we indented this li
    4099           0 :         indentedLI = listItem;
    4100             :       } else {
    4101             :         // need to make a blockquote to put things in if we haven't already,
    4102             :         // or if this node doesn't go in blockquote we used earlier.
    4103             :         // One reason it might not go in prio blockquote is if we are now
    4104             :         // in a different table cell.
    4105           0 :         if (curQuote && InDifferentTableElements(curQuote, curNode)) {
    4106           0 :           curQuote = nullptr;
    4107             :         }
    4108             : 
    4109           0 :         if (!curQuote) {
    4110             :           // First, check that our element can contain a blockquote.
    4111           0 :           if (NS_WARN_IF(!mHTMLEditor)) {
    4112           0 :             return NS_ERROR_UNEXPECTED;
    4113             :           }
    4114           0 :           if (!mHTMLEditor->CanContainTag(*curParent, *nsGkAtoms::blockquote)) {
    4115           0 :             return NS_OK; // cancelled
    4116             :           }
    4117             : 
    4118           0 :           rv = SplitAsNeeded(*nsGkAtoms::blockquote, curParent, offset);
    4119           0 :           NS_ENSURE_SUCCESS(rv, rv);
    4120           0 :           NS_ENSURE_STATE(mHTMLEditor);
    4121           0 :           curQuote = mHTMLEditor->CreateNode(nsGkAtoms::blockquote, curParent,
    4122           0 :                                              offset);
    4123           0 :           NS_ENSURE_STATE(curQuote);
    4124             :           // remember our new block for postprocessing
    4125           0 :           mNewBlock = curQuote;
    4126             :           // curQuote is now the correct thing to put curNode in
    4127             :         }
    4128             : 
    4129             :         // tuck the node into the end of the active blockquote
    4130           0 :         NS_ENSURE_STATE(mHTMLEditor);
    4131           0 :         rv = mHTMLEditor->MoveNode(curNode, curQuote, -1);
    4132           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4133             :         // forget curList, if any
    4134           0 :         curList = nullptr;
    4135             :       }
    4136             :     }
    4137             :   }
    4138           0 :   return NS_OK;
    4139             : }
    4140             : 
    4141             : 
    4142             : nsresult
    4143           0 : HTMLEditRules::WillOutdent(Selection& aSelection,
    4144             :                            bool* aCancel,
    4145             :                            bool* aHandled)
    4146             : {
    4147           0 :   MOZ_ASSERT(aCancel && aHandled);
    4148           0 :   *aCancel = false;
    4149           0 :   *aHandled = true;
    4150           0 :   nsCOMPtr<nsIContent> rememberedLeftBQ, rememberedRightBQ;
    4151           0 :   NS_ENSURE_STATE(mHTMLEditor);
    4152           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    4153           0 :   bool useCSS = htmlEditor->IsCSSEnabled();
    4154             : 
    4155           0 :   nsresult rv = NormalizeSelection(&aSelection);
    4156           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4157             : 
    4158             :   // Some scoping for selection resetting - we may need to tweak it
    4159             :   {
    4160           0 :     AutoSelectionRestorer selectionRestorer(&aSelection, htmlEditor);
    4161             : 
    4162             :     // Convert the selection ranges into "promoted" selection ranges: this
    4163             :     // basically just expands the range to include the immediate block parent,
    4164             :     // and then further expands to include any ancestors whose children are all
    4165             :     // in the range
    4166           0 :     nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
    4167           0 :     rv = GetNodesFromSelection(aSelection, EditAction::outdent, arrayOfNodes);
    4168           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4169             : 
    4170             :     // Okay, now go through all the nodes and remove a level of blockquoting,
    4171             :     // or whatever is appropriate.  Wohoo!
    4172             : 
    4173           0 :     nsCOMPtr<Element> curBlockQuote;
    4174           0 :     nsCOMPtr<nsIContent> firstBQChild, lastBQChild;
    4175           0 :     bool curBlockQuoteIsIndentedWithCSS = false;
    4176           0 :     for (uint32_t i = 0; i < arrayOfNodes.Length(); i++) {
    4177           0 :       if (!arrayOfNodes[i]->IsContent()) {
    4178           0 :         continue;
    4179             :       }
    4180           0 :       OwningNonNull<nsIContent> curNode = *arrayOfNodes[i]->AsContent();
    4181             : 
    4182             :       // Here's where we actually figure out what to do
    4183           0 :       nsCOMPtr<nsINode> curParent = curNode->GetParentNode();
    4184           0 :       int32_t offset = curParent ? curParent->IndexOf(curNode) : -1;
    4185             : 
    4186             :       // Is it a blockquote?
    4187           0 :       if (curNode->IsHTMLElement(nsGkAtoms::blockquote)) {
    4188             :         // If it is a blockquote, remove it.  So we need to finish up dealng
    4189             :         // with any curBlockQuote first.
    4190           0 :         if (curBlockQuote) {
    4191           0 :           rv = OutdentPartOfBlock(*curBlockQuote, *firstBQChild, *lastBQChild,
    4192             :                                   curBlockQuoteIsIndentedWithCSS,
    4193           0 :                                   getter_AddRefs(rememberedLeftBQ),
    4194           0 :                                   getter_AddRefs(rememberedRightBQ));
    4195           0 :           NS_ENSURE_SUCCESS(rv, rv);
    4196           0 :           curBlockQuote = nullptr;
    4197           0 :           firstBQChild = nullptr;
    4198           0 :           lastBQChild = nullptr;
    4199           0 :           curBlockQuoteIsIndentedWithCSS = false;
    4200             :         }
    4201           0 :         rv = htmlEditor->RemoveBlockContainer(curNode);
    4202           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4203           0 :         continue;
    4204             :       }
    4205             :       // Is it a block with a 'margin' property?
    4206           0 :       if (useCSS && IsBlockNode(curNode)) {
    4207             :         nsIAtom& marginProperty =
    4208           0 :           MarginPropertyAtomForIndent(*htmlEditor->mCSSEditUtils, curNode);
    4209           0 :         nsAutoString value;
    4210           0 :         htmlEditor->mCSSEditUtils->GetSpecifiedProperty(curNode,
    4211             :                                                          marginProperty,
    4212           0 :                                                          value);
    4213             :         float f;
    4214           0 :         nsCOMPtr<nsIAtom> unit;
    4215           0 :         NS_ENSURE_STATE(htmlEditor);
    4216           0 :         htmlEditor->mCSSEditUtils->ParseLength(value, &f,
    4217           0 :                                                getter_AddRefs(unit));
    4218           0 :         if (f > 0) {
    4219           0 :           ChangeIndentation(*curNode->AsElement(), Change::minus);
    4220           0 :           continue;
    4221             :         }
    4222             :       }
    4223             :       // Is it a list item?
    4224           0 :       if (HTMLEditUtils::IsListItem(curNode)) {
    4225             :         // If it is a list item, that means we are not outdenting whole list.
    4226             :         // So we need to finish up dealing with any curBlockQuote, and then pop
    4227             :         // this list item.
    4228           0 :         if (curBlockQuote) {
    4229           0 :           rv = OutdentPartOfBlock(*curBlockQuote, *firstBQChild, *lastBQChild,
    4230             :                                   curBlockQuoteIsIndentedWithCSS,
    4231           0 :                                   getter_AddRefs(rememberedLeftBQ),
    4232           0 :                                   getter_AddRefs(rememberedRightBQ));
    4233           0 :           NS_ENSURE_SUCCESS(rv, rv);
    4234           0 :           curBlockQuote = nullptr;
    4235           0 :           firstBQChild = nullptr;
    4236           0 :           lastBQChild = nullptr;
    4237           0 :           curBlockQuoteIsIndentedWithCSS = false;
    4238             :         }
    4239           0 :         rv = PopListItem(*curNode->AsContent());
    4240           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4241           0 :         continue;
    4242             :       }
    4243             :       // Do we have a blockquote that we are already committed to removing?
    4244           0 :       if (curBlockQuote) {
    4245             :         // If so, is this node a descendant?
    4246           0 :         if (EditorUtils::IsDescendantOf(curNode, curBlockQuote)) {
    4247           0 :           lastBQChild = curNode;
    4248             :           // Then we don't need to do anything different for this node
    4249           0 :           continue;
    4250             :         }
    4251             :         // Otherwise, we have progressed beyond end of curBlockQuote, so
    4252             :         // let's handle it now.  We need to remove the portion of
    4253             :         // curBlockQuote that contains [firstBQChild - lastBQChild].
    4254           0 :         rv = OutdentPartOfBlock(*curBlockQuote, *firstBQChild, *lastBQChild,
    4255             :                                 curBlockQuoteIsIndentedWithCSS,
    4256           0 :                                 getter_AddRefs(rememberedLeftBQ),
    4257           0 :                                 getter_AddRefs(rememberedRightBQ));
    4258           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4259           0 :         curBlockQuote = nullptr;
    4260           0 :         firstBQChild = nullptr;
    4261           0 :         lastBQChild = nullptr;
    4262           0 :         curBlockQuoteIsIndentedWithCSS = false;
    4263             :         // Fall out and handle curNode
    4264             :       }
    4265             : 
    4266             :       // Are we inside a blockquote?
    4267           0 :       OwningNonNull<nsINode> n = curNode;
    4268           0 :       curBlockQuoteIsIndentedWithCSS = false;
    4269             :       // Keep looking up the hierarchy as long as we don't hit the body or the
    4270             :       // active editing host or a table element (other than an entire table)
    4271           0 :       while (!n->IsHTMLElement(nsGkAtoms::body) && 
    4272           0 :              htmlEditor->IsDescendantOfEditorRoot(n) &&
    4273           0 :              (n->IsHTMLElement(nsGkAtoms::table) ||
    4274           0 :               !HTMLEditUtils::IsTableElement(n))) {
    4275           0 :         if (!n->GetParentNode()) {
    4276           0 :           break;
    4277             :         }
    4278           0 :         n = *n->GetParentNode();
    4279           0 :         if (n->IsHTMLElement(nsGkAtoms::blockquote)) {
    4280             :           // If so, remember it and the first node we are taking out of it.
    4281           0 :           curBlockQuote = n->AsElement();
    4282           0 :           firstBQChild = curNode;
    4283           0 :           lastBQChild = curNode;
    4284           0 :           break;
    4285           0 :         } else if (useCSS) {
    4286             :           nsIAtom& marginProperty =
    4287           0 :             MarginPropertyAtomForIndent(*htmlEditor->mCSSEditUtils, curNode);
    4288           0 :           nsAutoString value;
    4289           0 :           htmlEditor->mCSSEditUtils->GetSpecifiedProperty(*n, marginProperty,
    4290           0 :                                                            value);
    4291             :           float f;
    4292           0 :           nsCOMPtr<nsIAtom> unit;
    4293           0 :           htmlEditor->mCSSEditUtils->ParseLength(value, &f, getter_AddRefs(unit));
    4294           0 :           if (f > 0 && !(HTMLEditUtils::IsList(curParent) &&
    4295           0 :                          HTMLEditUtils::IsList(curNode))) {
    4296           0 :             curBlockQuote = n->AsElement();
    4297           0 :             firstBQChild = curNode;
    4298           0 :             lastBQChild = curNode;
    4299           0 :             curBlockQuoteIsIndentedWithCSS = true;
    4300           0 :             break;
    4301             :           }
    4302             :         }
    4303             :       }
    4304             : 
    4305           0 :       if (!curBlockQuote) {
    4306             :         // Couldn't find enclosing blockquote.  Handle list cases.
    4307           0 :         if (HTMLEditUtils::IsList(curParent)) {
    4308             :           // Move node out of list
    4309           0 :           if (HTMLEditUtils::IsList(curNode)) {
    4310             :             // Just unwrap this sublist
    4311           0 :             rv = htmlEditor->RemoveBlockContainer(curNode);
    4312           0 :             NS_ENSURE_SUCCESS(rv, rv);
    4313             :           }
    4314             :           // handled list item case above
    4315           0 :         } else if (HTMLEditUtils::IsList(curNode)) {
    4316             :           // node is a list, but parent is non-list: move list items out
    4317           0 :           nsCOMPtr<nsIContent> child = curNode->GetLastChild();
    4318           0 :           while (child) {
    4319           0 :             if (HTMLEditUtils::IsListItem(child)) {
    4320           0 :               rv = PopListItem(*child);
    4321           0 :               NS_ENSURE_SUCCESS(rv, rv);
    4322           0 :             } else if (HTMLEditUtils::IsList(child)) {
    4323             :               // We have an embedded list, so move it out from under the parent
    4324             :               // list. Be sure to put it after the parent list because this
    4325             :               // loop iterates backwards through the parent's list of children.
    4326             : 
    4327           0 :               rv = htmlEditor->MoveNode(child, curParent, offset + 1);
    4328           0 :               NS_ENSURE_SUCCESS(rv, rv);
    4329             :             } else {
    4330             :               // Delete any non-list items for now
    4331           0 :               rv = htmlEditor->DeleteNode(child);
    4332           0 :               NS_ENSURE_SUCCESS(rv, rv);
    4333             :             }
    4334           0 :             child = curNode->GetLastChild();
    4335             :           }
    4336             :           // Delete the now-empty list
    4337           0 :           rv = htmlEditor->RemoveBlockContainer(curNode);
    4338           0 :           NS_ENSURE_SUCCESS(rv, rv);
    4339           0 :         } else if (useCSS) {
    4340           0 :           nsCOMPtr<Element> element;
    4341           0 :           if (curNode->GetAsText()) {
    4342             :             // We want to outdent the parent of text nodes
    4343           0 :             element = curNode->GetParentElement();
    4344           0 :           } else if (curNode->IsElement()) {
    4345           0 :             element = curNode->AsElement();
    4346             :           }
    4347           0 :           if (element) {
    4348           0 :             ChangeIndentation(*element, Change::minus);
    4349             :           }
    4350             :         }
    4351             :       }
    4352             :     }
    4353           0 :     if (curBlockQuote) {
    4354             :       // We have a blockquote we haven't finished handling
    4355           0 :       rv = OutdentPartOfBlock(*curBlockQuote, *firstBQChild, *lastBQChild,
    4356             :                               curBlockQuoteIsIndentedWithCSS,
    4357           0 :                               getter_AddRefs(rememberedLeftBQ),
    4358           0 :                               getter_AddRefs(rememberedRightBQ));
    4359           0 :       NS_ENSURE_SUCCESS(rv, rv);
    4360             :     }
    4361             :   }
    4362             :   // Make sure selection didn't stick to last piece of content in old bq (only
    4363             :   // a problem for collapsed selections)
    4364           0 :   if (rememberedLeftBQ || rememberedRightBQ) {
    4365           0 :     if (aSelection.Collapsed()) {
    4366             :       // Push selection past end of rememberedLeftBQ
    4367           0 :       NS_ENSURE_TRUE(aSelection.GetRangeAt(0), NS_OK);
    4368             :       nsCOMPtr<nsINode> startNode =
    4369           0 :         aSelection.GetRangeAt(0)->GetStartContainer();
    4370           0 :       int32_t startOffset = aSelection.GetRangeAt(0)->StartOffset();
    4371           0 :       if (rememberedLeftBQ &&
    4372           0 :           (startNode == rememberedLeftBQ ||
    4373           0 :            EditorUtils::IsDescendantOf(startNode, rememberedLeftBQ))) {
    4374             :         // Selection is inside rememberedLeftBQ - push it past it.
    4375           0 :         startNode = rememberedLeftBQ->GetParentNode();
    4376           0 :         startOffset = startNode ? 1 + startNode->IndexOf(rememberedLeftBQ) : 0;
    4377           0 :         aSelection.Collapse(startNode, startOffset);
    4378             :       }
    4379             :       // And pull selection before beginning of rememberedRightBQ
    4380           0 :       startNode = aSelection.GetRangeAt(0)->GetStartContainer();
    4381           0 :       startOffset = aSelection.GetRangeAt(0)->StartOffset();
    4382           0 :       if (rememberedRightBQ &&
    4383           0 :           (startNode == rememberedRightBQ ||
    4384           0 :            EditorUtils::IsDescendantOf(startNode, rememberedRightBQ))) {
    4385             :         // Selection is inside rememberedRightBQ - push it before it.
    4386           0 :         startNode = rememberedRightBQ->GetParentNode();
    4387           0 :         startOffset = startNode ? startNode->IndexOf(rememberedRightBQ) : -1;
    4388           0 :         aSelection.Collapse(startNode, startOffset);
    4389             :       }
    4390             :     }
    4391           0 :     return NS_OK;
    4392             :   }
    4393           0 :   return NS_OK;
    4394             : }
    4395             : 
    4396             : 
    4397             : /**
    4398             :  * RemovePartOfBlock() splits aBlock and move aStartChild to aEndChild out of
    4399             :  * aBlock.
    4400             :  */
    4401             : nsresult
    4402           0 : HTMLEditRules::RemovePartOfBlock(Element& aBlock,
    4403             :                                  nsIContent& aStartChild,
    4404             :                                  nsIContent& aEndChild)
    4405             : {
    4406           0 :   SplitBlock(aBlock, aStartChild, aEndChild);
    4407             :   // Get rid of part of blockquote we are outdenting
    4408             : 
    4409           0 :   NS_ENSURE_STATE(mHTMLEditor);
    4410           0 :   nsresult rv = mHTMLEditor->RemoveBlockContainer(aBlock);
    4411           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4412             : 
    4413           0 :   return NS_OK;
    4414             : }
    4415             : 
    4416             : void
    4417           0 : HTMLEditRules::SplitBlock(Element& aBlock,
    4418             :                           nsIContent& aStartChild,
    4419             :                           nsIContent& aEndChild,
    4420             :                           nsIContent** aOutLeftNode,
    4421             :                           nsIContent** aOutRightNode,
    4422             :                           nsIContent** aOutMiddleNode)
    4423             : {
    4424             :   // aStartChild and aEndChild must be exclusive descendants of aBlock
    4425           0 :   MOZ_ASSERT(EditorUtils::IsDescendantOf(&aStartChild, &aBlock) &&
    4426             :              EditorUtils::IsDescendantOf(&aEndChild, &aBlock));
    4427           0 :   NS_ENSURE_TRUE_VOID(mHTMLEditor);
    4428           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    4429             : 
    4430             :   // Get split point location
    4431           0 :   OwningNonNull<nsIContent> startParent = *aStartChild.GetParent();
    4432           0 :   int32_t startOffset = startParent->IndexOf(&aStartChild);
    4433             : 
    4434             :   // Do the splits!
    4435           0 :   nsCOMPtr<nsIContent> newMiddleNode1;
    4436           0 :   htmlEditor->SplitNodeDeep(aBlock, startParent, startOffset,
    4437             :                             HTMLEditor::EmptyContainers::no,
    4438           0 :                             aOutLeftNode, getter_AddRefs(newMiddleNode1));
    4439             : 
    4440             :   // Get split point location
    4441           0 :   OwningNonNull<nsIContent> endParent = *aEndChild.GetParent();
    4442             :   // +1 because we want to be after the child
    4443           0 :   int32_t endOffset = 1 + endParent->IndexOf(&aEndChild);
    4444             : 
    4445             :   // Do the splits!
    4446           0 :   nsCOMPtr<nsIContent> newMiddleNode2;
    4447           0 :   htmlEditor->SplitNodeDeep(aBlock, endParent, endOffset,
    4448             :                             HTMLEditor::EmptyContainers::no,
    4449           0 :                             getter_AddRefs(newMiddleNode2), aOutRightNode);
    4450             : 
    4451           0 :   if (aOutMiddleNode) {
    4452           0 :     if (newMiddleNode2) {
    4453           0 :       newMiddleNode2.forget(aOutMiddleNode);
    4454             :     } else {
    4455           0 :       newMiddleNode1.forget(aOutMiddleNode);
    4456             :     }
    4457             :   }
    4458             : }
    4459             : 
    4460             : nsresult
    4461           0 : HTMLEditRules::OutdentPartOfBlock(Element& aBlock,
    4462             :                                   nsIContent& aStartChild,
    4463             :                                   nsIContent& aEndChild,
    4464             :                                   bool aIsBlockIndentedWithCSS,
    4465             :                                   nsIContent** aOutLeftNode,
    4466             :                                   nsIContent** aOutRightNode)
    4467             : {
    4468           0 :   MOZ_ASSERT(aOutLeftNode && aOutRightNode);
    4469             : 
    4470           0 :   nsCOMPtr<nsIContent> middleNode;
    4471           0 :   SplitBlock(aBlock, aStartChild, aEndChild, aOutLeftNode, aOutRightNode,
    4472           0 :              getter_AddRefs(middleNode));
    4473             : 
    4474           0 :   NS_ENSURE_STATE(middleNode);
    4475             : 
    4476           0 :   if (!aIsBlockIndentedWithCSS) {
    4477           0 :     NS_ENSURE_STATE(mHTMLEditor);
    4478           0 :     nsresult rv = mHTMLEditor->RemoveBlockContainer(*middleNode);
    4479           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4480           0 :   } else if (middleNode->IsElement()) {
    4481             :     // We do nothing if middleNode isn't an element
    4482           0 :     nsresult rv = ChangeIndentation(*middleNode->AsElement(), Change::minus);
    4483           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4484             :   }
    4485             : 
    4486           0 :   return NS_OK;
    4487             : }
    4488             : 
    4489             : /**
    4490             :  * ConvertListType() converts list type and list item type.
    4491             :  */
    4492             : nsresult
    4493           0 : HTMLEditRules::ConvertListType(Element* aList,
    4494             :                                Element** aOutList,
    4495             :                                nsIAtom* aListType,
    4496             :                                nsIAtom* aItemType)
    4497             : {
    4498           0 :   MOZ_ASSERT(aList);
    4499           0 :   MOZ_ASSERT(aOutList);
    4500           0 :   MOZ_ASSERT(aListType);
    4501           0 :   MOZ_ASSERT(aItemType);
    4502             : 
    4503           0 :   nsCOMPtr<nsINode> child = aList->GetFirstChild();
    4504           0 :   while (child) {
    4505           0 :     if (child->IsElement()) {
    4506           0 :       dom::Element* element = child->AsElement();
    4507           0 :       if (HTMLEditUtils::IsListItem(element) &&
    4508           0 :           !element->IsHTMLElement(aItemType)) {
    4509           0 :         child = mHTMLEditor->ReplaceContainer(element, aItemType);
    4510           0 :         NS_ENSURE_STATE(child);
    4511           0 :       } else if (HTMLEditUtils::IsList(element) &&
    4512           0 :                  !element->IsHTMLElement(aListType)) {
    4513           0 :         nsCOMPtr<dom::Element> temp;
    4514           0 :         nsresult rv = ConvertListType(child->AsElement(), getter_AddRefs(temp),
    4515           0 :                                       aListType, aItemType);
    4516           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4517           0 :         child = temp.forget();
    4518             :       }
    4519             :     }
    4520           0 :     child = child->GetNextSibling();
    4521             :   }
    4522             : 
    4523           0 :   if (aList->IsHTMLElement(aListType)) {
    4524           0 :     nsCOMPtr<dom::Element> list = aList->AsElement();
    4525           0 :     list.forget(aOutList);
    4526           0 :     return NS_OK;
    4527             :   }
    4528             : 
    4529           0 :   *aOutList = mHTMLEditor->ReplaceContainer(aList, aListType).take();
    4530           0 :   NS_ENSURE_STATE(aOutList);
    4531             : 
    4532           0 :   return NS_OK;
    4533             : }
    4534             : 
    4535             : 
    4536             : /**
    4537             :  * CreateStyleForInsertText() takes care of clearing and setting appropriate
    4538             :  * style nodes for text insertion.
    4539             :  */
    4540             : nsresult
    4541           0 : HTMLEditRules::CreateStyleForInsertText(Selection& aSelection,
    4542             :                                         nsIDocument& aDoc)
    4543             : {
    4544           0 :   MOZ_ASSERT(mHTMLEditor->mTypeInState);
    4545             : 
    4546           0 :   bool weDidSomething = false;
    4547           0 :   NS_ENSURE_STATE(aSelection.GetRangeAt(0));
    4548           0 :   nsCOMPtr<nsINode> node = aSelection.GetRangeAt(0)->GetStartContainer();
    4549           0 :   int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
    4550             : 
    4551             :   // next examine our present style and make sure default styles are either
    4552             :   // present or explicitly overridden.  If neither, add the default style to
    4553             :   // the TypeInState
    4554           0 :   int32_t length = mHTMLEditor->mDefaultStyles.Length();
    4555           0 :   for (int32_t j = 0; j < length; j++) {
    4556           0 :     PropItem* propItem = mHTMLEditor->mDefaultStyles[j];
    4557           0 :     MOZ_ASSERT(propItem);
    4558             :     bool bFirst, bAny, bAll;
    4559             : 
    4560             :     // GetInlineProperty also examine TypeInState.  The only gotcha here is
    4561             :     // that a cleared property looks like an unset property.  For now I'm
    4562             :     // assuming that's not a problem: that default styles will always be
    4563             :     // multivalue styles (like font face or size) where clearing the style
    4564             :     // means we want to go back to the default.  If we ever wanted a "toggle"
    4565             :     // style like bold for a default, though, I'll have to add code to detect
    4566             :     // the difference between unset and explicitly cleared, else user would
    4567             :     // never be able to unbold, for instance.
    4568           0 :     nsAutoString curValue;
    4569           0 :     NS_ENSURE_STATE(mHTMLEditor);
    4570             :     nsresult rv =
    4571           0 :       mHTMLEditor->GetInlinePropertyBase(*propItem->tag, &propItem->attr,
    4572             :                                          nullptr, &bFirst, &bAny, &bAll,
    4573           0 :                                          &curValue, false);
    4574           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4575             : 
    4576           0 :     if (!bAny) {
    4577             :       // no style set for this prop/attr
    4578           0 :       mHTMLEditor->mTypeInState->SetProp(propItem->tag, propItem->attr,
    4579           0 :                                          propItem->value);
    4580             :     }
    4581             :   }
    4582             : 
    4583           0 :   nsCOMPtr<Element> rootElement = aDoc.GetRootElement();
    4584           0 :   NS_ENSURE_STATE(rootElement);
    4585             : 
    4586             :   // process clearing any styles first
    4587             :   UniquePtr<PropItem> item =
    4588           0 :     Move(mHTMLEditor->mTypeInState->TakeClearProperty());
    4589           0 :   while (item && node != rootElement) {
    4590           0 :     NS_ENSURE_STATE(mHTMLEditor);
    4591             :     nsresult rv =
    4592           0 :       mHTMLEditor->ClearStyle(address_of(node), &offset,
    4593           0 :                               item->tag, &item->attr);
    4594           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4595           0 :     item = Move(mHTMLEditor->mTypeInState->TakeClearProperty());
    4596           0 :     weDidSomething = true;
    4597             :   }
    4598             : 
    4599             :   // then process setting any styles
    4600           0 :   int32_t relFontSize = mHTMLEditor->mTypeInState->TakeRelativeFontSize();
    4601           0 :   item = Move(mHTMLEditor->mTypeInState->TakeSetProperty());
    4602             : 
    4603           0 :   if (item || relFontSize) {
    4604             :     // we have at least one style to add; make a new text node to insert style
    4605             :     // nodes above.
    4606           0 :     if (RefPtr<Text> text = node->GetAsText()) {
    4607             :       // if we are in a text node, split it
    4608           0 :       NS_ENSURE_STATE(mHTMLEditor);
    4609           0 :       offset = mHTMLEditor->SplitNodeDeep(*text, *text, offset);
    4610           0 :       NS_ENSURE_STATE(offset != -1);
    4611           0 :       node = node->GetParentNode();
    4612             :     }
    4613           0 :     if (!mHTMLEditor->IsContainer(node)) {
    4614           0 :       return NS_OK;
    4615             :     }
    4616           0 :     OwningNonNull<Text> newNode = aDoc.CreateTextNode(EmptyString());
    4617           0 :     NS_ENSURE_STATE(mHTMLEditor);
    4618           0 :     nsresult rv = mHTMLEditor->InsertNode(*newNode, *node, offset);
    4619           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4620           0 :     node = newNode;
    4621           0 :     offset = 0;
    4622           0 :     weDidSomething = true;
    4623             : 
    4624           0 :     if (relFontSize) {
    4625             :       // dir indicated bigger versus smaller.  1 = bigger, -1 = smaller
    4626           0 :       HTMLEditor::FontSize dir = relFontSize > 0 ?
    4627           0 :         HTMLEditor::FontSize::incr : HTMLEditor::FontSize::decr;
    4628           0 :       for (int32_t j = 0; j < DeprecatedAbs(relFontSize); j++) {
    4629           0 :         NS_ENSURE_STATE(mHTMLEditor);
    4630           0 :         rv = mHTMLEditor->RelativeFontChangeOnTextNode(dir, newNode, 0, -1);
    4631           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4632             :       }
    4633             :     }
    4634             : 
    4635           0 :     while (item) {
    4636           0 :       NS_ENSURE_STATE(mHTMLEditor);
    4637           0 :       rv = mHTMLEditor->SetInlinePropertyOnNode(*node->AsContent(),
    4638           0 :                                                 *item->tag, &item->attr,
    4639           0 :                                                 item->value);
    4640           0 :       NS_ENSURE_SUCCESS(rv, rv);
    4641           0 :       item = mHTMLEditor->mTypeInState->TakeSetProperty();
    4642             :     }
    4643             :   }
    4644           0 :   if (weDidSomething) {
    4645           0 :     return aSelection.Collapse(node, offset);
    4646             :   }
    4647             : 
    4648           0 :   return NS_OK;
    4649             : }
    4650             : 
    4651             : 
    4652             : /**
    4653             :  * Figure out if aNode is (or is inside) an empty block.  A block can have
    4654             :  * children and still be considered empty, if the children are empty or
    4655             :  * non-editable.
    4656             :  */
    4657             : nsresult
    4658           0 : HTMLEditRules::IsEmptyBlock(Element& aNode,
    4659             :                             bool* aOutIsEmptyBlock,
    4660             :                             MozBRCounts aMozBRCounts)
    4661             : {
    4662           0 :   MOZ_ASSERT(aOutIsEmptyBlock);
    4663           0 :   *aOutIsEmptyBlock = true;
    4664             : 
    4665           0 :   NS_ENSURE_TRUE(IsBlockNode(aNode), NS_ERROR_NULL_POINTER);
    4666             : 
    4667           0 :   return mHTMLEditor->IsEmptyNode(aNode.AsDOMNode(), aOutIsEmptyBlock,
    4668             :                                   aMozBRCounts == MozBRCounts::yes ? false
    4669           0 :                                                                    : true);
    4670             : }
    4671             : 
    4672             : 
    4673             : nsresult
    4674           0 : HTMLEditRules::WillAlign(Selection& aSelection,
    4675             :                          const nsAString& aAlignType,
    4676             :                          bool* aCancel,
    4677             :                          bool* aHandled)
    4678             : {
    4679           0 :   MOZ_ASSERT(aCancel && aHandled);
    4680             : 
    4681           0 :   NS_ENSURE_STATE(mHTMLEditor);
    4682           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    4683             : 
    4684           0 :   WillInsert(aSelection, aCancel);
    4685             : 
    4686             :   // Initialize out param.  We want to ignore result of WillInsert().
    4687           0 :   *aCancel = false;
    4688           0 :   *aHandled = false;
    4689             : 
    4690           0 :   nsresult rv = NormalizeSelection(&aSelection);
    4691           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4692           0 :   AutoSelectionRestorer selectionRestorer(&aSelection, htmlEditor);
    4693             : 
    4694             :   // Convert the selection ranges into "promoted" selection ranges: This
    4695             :   // basically just expands the range to include the immediate block parent,
    4696             :   // and then further expands to include any ancestors whose children are all
    4697             :   // in the range
    4698           0 :   *aHandled = true;
    4699           0 :   nsTArray<OwningNonNull<nsINode>> nodeArray;
    4700           0 :   rv = GetNodesFromSelection(aSelection, EditAction::align, nodeArray);
    4701           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4702             : 
    4703             :   // If we don't have any nodes, or we have only a single br, then we are
    4704             :   // creating an empty alignment div.  We have to do some different things for
    4705             :   // these.
    4706           0 :   bool emptyDiv = nodeArray.IsEmpty();
    4707           0 :   if (nodeArray.Length() == 1) {
    4708           0 :     OwningNonNull<nsINode> node = nodeArray[0];
    4709             : 
    4710           0 :     if (HTMLEditUtils::SupportsAlignAttr(*node)) {
    4711             :       // The node is a table element, an hr, a paragraph, a div or a section
    4712             :       // header; in HTML 4, it can directly carry the ALIGN attribute and we
    4713             :       // don't need to make a div! If we are in CSS mode, all the work is done
    4714             :       // in AlignBlock
    4715           0 :       rv = AlignBlock(*node->AsElement(), aAlignType, ContentsOnly::yes);
    4716           0 :       NS_ENSURE_SUCCESS(rv, rv);
    4717           0 :       return NS_OK;
    4718             :     }
    4719             : 
    4720           0 :     if (TextEditUtils::IsBreak(node)) {
    4721             :       // The special case emptyDiv code (below) that consumes BRs can cause
    4722             :       // tables to split if the start node of the selection is not in a table
    4723             :       // cell or caption, for example parent is a <tr>.  Avoid this unnecessary
    4724             :       // splitting if possible by leaving emptyDiv FALSE so that we fall
    4725             :       // through to the normal case alignment code.
    4726             :       //
    4727             :       // XXX: It seems a little error prone for the emptyDiv special case code
    4728             :       // to assume that the start node of the selection is the parent of the
    4729             :       // single node in the nodeArray, as the paragraph above points out. Do we
    4730             :       // rely on the selection start node because of the fact that nodeArray
    4731             :       // can be empty?  We should probably revisit this issue. - kin
    4732             : 
    4733           0 :       NS_ENSURE_STATE(aSelection.GetRangeAt(0) &&
    4734             :                       aSelection.GetRangeAt(0)->GetStartContainer());
    4735             :       OwningNonNull<nsINode> parent =
    4736           0 :         *aSelection.GetRangeAt(0)->GetStartContainer();
    4737             : 
    4738           0 :       emptyDiv = !HTMLEditUtils::IsTableElement(parent) ||
    4739           0 :                  HTMLEditUtils::IsTableCellOrCaption(parent);
    4740             :     }
    4741             :   }
    4742           0 :   if (emptyDiv) {
    4743             :     nsCOMPtr<nsINode> parent =
    4744           0 :       aSelection.GetRangeAt(0) ? aSelection.GetRangeAt(0)->GetStartContainer()
    4745           0 :                                : nullptr;
    4746           0 :     NS_ENSURE_STATE(parent);
    4747           0 :     int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
    4748             : 
    4749           0 :     rv = SplitAsNeeded(*nsGkAtoms::div, parent, offset);
    4750           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4751             :     // Consume a trailing br, if any.  This is to keep an alignment from
    4752             :     // creating extra lines, if possible.
    4753             :     nsCOMPtr<nsIContent> brContent =
    4754           0 :       htmlEditor->GetNextHTMLNode(parent, offset);
    4755           0 :     if (brContent && TextEditUtils::IsBreak(brContent)) {
    4756             :       // Making use of html structure... if next node after where we are
    4757             :       // putting our div is not a block, then the br we found is in same block
    4758             :       // we are, so it's safe to consume it.
    4759             :       nsCOMPtr<nsIContent> sibling = htmlEditor->GetNextHTMLSibling(parent,
    4760           0 :                                                                     offset);
    4761           0 :       if (sibling && !IsBlockNode(*sibling)) {
    4762           0 :         rv = htmlEditor->DeleteNode(brContent);
    4763           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4764             :       }
    4765             :     }
    4766           0 :     nsCOMPtr<Element> div = htmlEditor->CreateNode(nsGkAtoms::div, parent,
    4767           0 :                                                    offset);
    4768           0 :     NS_ENSURE_STATE(div);
    4769             :     // Remember our new block for postprocessing
    4770           0 :     mNewBlock = div;
    4771             :     // Set up the alignment on the div, using HTML or CSS
    4772           0 :     rv = AlignBlock(*div, aAlignType, ContentsOnly::yes);
    4773           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4774           0 :     *aHandled = true;
    4775             :     // Put in a moz-br so that it won't get deleted
    4776           0 :     rv = CreateMozBR(div->AsDOMNode(), 0);
    4777           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4778           0 :     rv = aSelection.Collapse(div, 0);
    4779             :     // Don't restore the selection
    4780           0 :     selectionRestorer.Abort();
    4781           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4782           0 :     return NS_OK;
    4783             :   }
    4784             : 
    4785             :   // Next we detect all the transitions in the array, where a transition
    4786             :   // means that adjacent nodes in the array don't have the same parent.
    4787             : 
    4788           0 :   nsTArray<bool> transitionList;
    4789           0 :   MakeTransitionList(nodeArray, transitionList);
    4790             : 
    4791             :   // Okay, now go through all the nodes and give them an align attrib or put
    4792             :   // them in a div, or whatever is appropriate.  Woohoo!
    4793             : 
    4794           0 :   nsCOMPtr<Element> curDiv;
    4795           0 :   bool useCSS = htmlEditor->IsCSSEnabled();
    4796           0 :   for (size_t i = 0; i < nodeArray.Length(); i++) {
    4797           0 :     auto& curNode = nodeArray[i];
    4798             :     // Here's where we actually figure out what to do
    4799             : 
    4800             :     // Ignore all non-editable nodes.  Leave them be.
    4801           0 :     if (!htmlEditor->IsEditable(curNode)) {
    4802           0 :       continue;
    4803             :     }
    4804             : 
    4805             :     // The node is a table element, an hr, a paragraph, a div or a section
    4806             :     // header; in HTML 4, it can directly carry the ALIGN attribute and we
    4807             :     // don't need to nest it, just set the alignment.  In CSS, assign the
    4808             :     // corresponding CSS styles in AlignBlock
    4809           0 :     if (HTMLEditUtils::SupportsAlignAttr(*curNode)) {
    4810           0 :       rv = AlignBlock(*curNode->AsElement(), aAlignType, ContentsOnly::no);
    4811           0 :       NS_ENSURE_SUCCESS(rv, rv);
    4812             :       // Clear out curDiv so that we don't put nodes after this one into it
    4813           0 :       curDiv = nullptr;
    4814           0 :       continue;
    4815             :     }
    4816             : 
    4817           0 :     nsCOMPtr<nsINode> curParent = curNode->GetParentNode();
    4818           0 :     int32_t offset = curParent ? curParent->IndexOf(curNode) : -1;
    4819             : 
    4820             :     // Skip insignificant formatting text nodes to prevent unnecessary
    4821             :     // structure splitting!
    4822           0 :     bool isEmptyTextNode = false;
    4823           0 :     if (curNode->GetAsText() &&
    4824           0 :         ((HTMLEditUtils::IsTableElement(curParent) &&
    4825           0 :           !HTMLEditUtils::IsTableCellOrCaption(*curParent)) ||
    4826           0 :          HTMLEditUtils::IsList(curParent) ||
    4827           0 :          (NS_SUCCEEDED(htmlEditor->IsEmptyNode(curNode, &isEmptyTextNode)) &&
    4828             :           isEmptyTextNode))) {
    4829           0 :       continue;
    4830             :     }
    4831             : 
    4832             :     // If it's a list item, or a list inside a list, forget any "current" div,
    4833             :     // and instead put divs inside the appropriate block (td, li, etc.)
    4834           0 :     if (HTMLEditUtils::IsListItem(curNode) ||
    4835           0 :         HTMLEditUtils::IsList(curNode)) {
    4836           0 :       rv = RemoveAlignment(*curNode, aAlignType, true);
    4837           0 :       NS_ENSURE_SUCCESS(rv, rv);
    4838           0 :       if (useCSS) {
    4839           0 :         htmlEditor->mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
    4840             :             curNode->AsElement(), nullptr, nsGkAtoms::align,
    4841           0 :             &aAlignType, false);
    4842           0 :         curDiv = nullptr;
    4843           0 :         continue;
    4844             :       }
    4845           0 :       if (HTMLEditUtils::IsList(curParent)) {
    4846             :         // If we don't use CSS, add a contraint to list element: they have to
    4847             :         // be inside another list, i.e., >= second level of nesting
    4848           0 :         rv = AlignInnerBlocks(*curNode, &aAlignType);
    4849           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4850           0 :         curDiv = nullptr;
    4851           0 :         continue;
    4852             :       }
    4853             :       // Clear out curDiv so that we don't put nodes after this one into it
    4854             :     }
    4855             : 
    4856             :     // Need to make a div to put things in if we haven't already, or if this
    4857             :     // node doesn't go in div we used earlier.
    4858           0 :     if (!curDiv || transitionList[i]) {
    4859             :       // First, check that our element can contain a div.
    4860           0 :       if (!htmlEditor->CanContainTag(*curParent, *nsGkAtoms::div)) {
    4861             :         // Cancelled
    4862           0 :         return NS_OK;
    4863             :       }
    4864             : 
    4865           0 :       rv = SplitAsNeeded(*nsGkAtoms::div, curParent, offset);
    4866           0 :       NS_ENSURE_SUCCESS(rv, rv);
    4867           0 :       curDiv = htmlEditor->CreateNode(nsGkAtoms::div, curParent, offset);
    4868           0 :       NS_ENSURE_STATE(curDiv);
    4869             :       // Remember our new block for postprocessing
    4870           0 :       mNewBlock = curDiv;
    4871             :       // Set up the alignment on the div
    4872           0 :       rv = AlignBlock(*curDiv, aAlignType, ContentsOnly::yes);
    4873             :     }
    4874             : 
    4875           0 :     NS_ENSURE_STATE(curNode->IsContent());
    4876             : 
    4877             :     // Tuck the node into the end of the active div
    4878           0 :     rv = htmlEditor->MoveNode(curNode->AsContent(), curDiv, -1);
    4879           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4880             :   }
    4881             : 
    4882           0 :   return NS_OK;
    4883             : }
    4884             : 
    4885             : 
    4886             : /**
    4887             :  * AlignInnerBlocks() aligns inside table cells or list items.
    4888             :  */
    4889             : nsresult
    4890           0 : HTMLEditRules::AlignInnerBlocks(nsINode& aNode,
    4891             :                                 const nsAString* alignType)
    4892             : {
    4893           0 :   NS_ENSURE_TRUE(alignType, NS_ERROR_NULL_POINTER);
    4894             : 
    4895             :   // Gather list of table cells or list items
    4896           0 :   nsTArray<OwningNonNull<nsINode>> nodeArray;
    4897           0 :   TableCellAndListItemFunctor functor;
    4898           0 :   DOMIterator iter(aNode);
    4899           0 :   iter.AppendList(functor, nodeArray);
    4900             : 
    4901             :   // Now that we have the list, align their contents as requested
    4902           0 :   for (auto& node : nodeArray) {
    4903           0 :     nsresult rv = AlignBlockContents(GetAsDOMNode(node), alignType);
    4904           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4905             :   }
    4906             : 
    4907           0 :   return NS_OK;
    4908             : }
    4909             : 
    4910             : 
    4911             : /**
    4912             :  * AlignBlockContents() aligns contents of a block element.
    4913             :  */
    4914             : nsresult
    4915           0 : HTMLEditRules::AlignBlockContents(nsIDOMNode* aNode,
    4916             :                                   const nsAString* alignType)
    4917             : {
    4918           0 :   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
    4919           0 :   NS_ENSURE_TRUE(node && alignType, NS_ERROR_NULL_POINTER);
    4920           0 :   nsCOMPtr<nsIContent> firstChild, lastChild;
    4921             : 
    4922           0 :   bool useCSS = mHTMLEditor->IsCSSEnabled();
    4923             : 
    4924           0 :   NS_ENSURE_STATE(mHTMLEditor);
    4925           0 :   firstChild = mHTMLEditor->GetFirstEditableChild(*node);
    4926           0 :   NS_ENSURE_STATE(mHTMLEditor);
    4927           0 :   lastChild = mHTMLEditor->GetLastEditableChild(*node);
    4928           0 :   if (!firstChild) {
    4929             :     // this cell has no content, nothing to align
    4930           0 :   } else if (firstChild == lastChild &&
    4931           0 :              firstChild->IsHTMLElement(nsGkAtoms::div)) {
    4932             :     // the cell already has a div containing all of its content: just
    4933             :     // act on this div.
    4934           0 :     RefPtr<Element> divElem = firstChild->AsElement();
    4935           0 :     if (useCSS) {
    4936           0 :       NS_ENSURE_STATE(mHTMLEditor);
    4937           0 :       nsresult rv = mHTMLEditor->SetAttributeOrEquivalent(divElem,
    4938             :                                                           nsGkAtoms::align,
    4939           0 :                                                           *alignType, false);
    4940           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    4941           0 :         return rv;
    4942             :       }
    4943             :     } else {
    4944           0 :       NS_ENSURE_STATE(mHTMLEditor);
    4945           0 :       nsresult rv = mHTMLEditor->SetAttribute(divElem, nsGkAtoms::align,
    4946           0 :                                               *alignType);
    4947           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    4948           0 :         return rv;
    4949             :       }
    4950             :     }
    4951             :   } else {
    4952             :     // else we need to put in a div, set the alignment, and toss in all the children
    4953           0 :     NS_ENSURE_STATE(mHTMLEditor);
    4954           0 :     RefPtr<Element> divElem = mHTMLEditor->CreateNode(nsGkAtoms::div, node, 0);
    4955           0 :     NS_ENSURE_STATE(divElem);
    4956             :     // set up the alignment on the div
    4957           0 :     if (useCSS) {
    4958           0 :       NS_ENSURE_STATE(mHTMLEditor);
    4959             :       nsresult rv =
    4960           0 :         mHTMLEditor->SetAttributeOrEquivalent(divElem, nsGkAtoms::align,
    4961           0 :                                               *alignType, false);
    4962           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    4963           0 :         return rv;
    4964             :       }
    4965             :     } else {
    4966           0 :       NS_ENSURE_STATE(mHTMLEditor);
    4967             :       nsresult rv =
    4968           0 :         mHTMLEditor->SetAttribute(divElem, nsGkAtoms::align, *alignType);
    4969           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    4970           0 :         return rv;
    4971             :       }
    4972             :     }
    4973             :     // tuck the children into the end of the active div
    4974           0 :     while (lastChild && (lastChild != divElem)) {
    4975           0 :       NS_ENSURE_STATE(mHTMLEditor);
    4976           0 :       nsresult rv = mHTMLEditor->MoveNode(lastChild, divElem, 0);
    4977           0 :       NS_ENSURE_SUCCESS(rv, rv);
    4978           0 :       NS_ENSURE_STATE(mHTMLEditor);
    4979           0 :       lastChild = mHTMLEditor->GetLastEditableChild(*node);
    4980             :     }
    4981             :   }
    4982           0 :   return NS_OK;
    4983             : }
    4984             : 
    4985             : /**
    4986             :  * CheckForEmptyBlock() is called by WillDeleteSelection() to detect and handle
    4987             :  * case of deleting from inside an empty block.
    4988             :  */
    4989             : nsresult
    4990           0 : HTMLEditRules::CheckForEmptyBlock(nsINode* aStartNode,
    4991             :                                   Element* aBodyNode,
    4992             :                                   Selection* aSelection,
    4993             :                                   nsIEditor::EDirection aAction,
    4994             :                                   bool* aHandled)
    4995             : {
    4996             :   // If the editing host is an inline element, bail out early.
    4997           0 :   if (aBodyNode && IsInlineNode(*aBodyNode)) {
    4998           0 :     return NS_OK;
    4999             :   }
    5000           0 :   NS_ENSURE_STATE(mHTMLEditor);
    5001           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    5002             : 
    5003             :   // If we are inside an empty block, delete it.  Note: do NOT delete table
    5004             :   // elements this way.
    5005           0 :   nsCOMPtr<Element> block = htmlEditor->GetBlock(*aStartNode);
    5006             :   bool bIsEmptyNode;
    5007           0 :   nsCOMPtr<Element> emptyBlock;
    5008           0 :   if (block && block != aBodyNode) {
    5009             :     // Efficiency hack, avoiding IsEmptyNode() call when in body
    5010           0 :     nsresult rv = htmlEditor->IsEmptyNode(block, &bIsEmptyNode, true, false);
    5011           0 :     NS_ENSURE_SUCCESS(rv, rv);
    5012           0 :     while (block && bIsEmptyNode && !HTMLEditUtils::IsTableElement(block) &&
    5013           0 :            block != aBodyNode) {
    5014           0 :       emptyBlock = block;
    5015           0 :       block = htmlEditor->GetBlockNodeParent(emptyBlock);
    5016           0 :       if (block) {
    5017           0 :         rv = htmlEditor->IsEmptyNode(block, &bIsEmptyNode, true, false);
    5018           0 :         NS_ENSURE_SUCCESS(rv, rv);
    5019             :       }
    5020             :     }
    5021             :   }
    5022             : 
    5023           0 :   if (emptyBlock && emptyBlock->IsEditable()) {
    5024           0 :     nsCOMPtr<nsINode> blockParent = emptyBlock->GetParentNode();
    5025           0 :     NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE);
    5026           0 :     int32_t offset = blockParent->IndexOf(emptyBlock);
    5027             : 
    5028           0 :     if (HTMLEditUtils::IsListItem(emptyBlock)) {
    5029             :       // Are we the first list item in the list?
    5030           0 :       NS_ENSURE_STATE(htmlEditor);
    5031           0 :       if (htmlEditor->IsFirstEditableChild(emptyBlock)) {
    5032           0 :         nsCOMPtr<nsINode> listParent = blockParent->GetParentNode();
    5033           0 :         NS_ENSURE_TRUE(listParent, NS_ERROR_FAILURE);
    5034           0 :         int32_t listOffset = listParent->IndexOf(blockParent);
    5035             :         // If we are a sublist, skip the br creation
    5036           0 :         if (!HTMLEditUtils::IsList(listParent)) {
    5037             :           // Create a br before list
    5038           0 :           NS_ENSURE_STATE(htmlEditor);
    5039             :           nsCOMPtr<Element> br =
    5040           0 :             htmlEditor->CreateBR(listParent, listOffset);
    5041           0 :           NS_ENSURE_STATE(br);
    5042             :           // Adjust selection to be right before it
    5043           0 :           nsresult rv = aSelection->Collapse(listParent, listOffset);
    5044           0 :           NS_ENSURE_SUCCESS(rv, rv);
    5045             :         }
    5046             :         // Else just let selection percolate up.  We'll adjust it in
    5047             :         // AfterEdit()
    5048             :       }
    5049             :     } else {
    5050           0 :       if (aAction == nsIEditor::eNext || aAction == nsIEditor::eNextWord ||
    5051             :           aAction == nsIEditor::eToEndOfLine) {
    5052             :         // Move to the start of the next node, if any
    5053           0 :         nsCOMPtr<nsIContent> nextNode = htmlEditor->GetNextNode(blockParent,
    5054           0 :                                                                 offset + 1, true);
    5055           0 :         if (nextNode) {
    5056           0 :           EditorDOMPoint pt = GetGoodSelPointForNode(*nextNode, aAction);
    5057           0 :           nsresult rv = aSelection->Collapse(pt.node, pt.offset);
    5058           0 :           NS_ENSURE_SUCCESS(rv, rv);
    5059             :         } else {
    5060             :           // Adjust selection to be right after it.
    5061           0 :           nsresult rv = aSelection->Collapse(blockParent, offset + 1);
    5062           0 :           NS_ENSURE_SUCCESS(rv, rv);
    5063           0 :         }
    5064           0 :       } else if (aAction == nsIEditor::ePrevious ||
    5065           0 :                  aAction == nsIEditor::ePreviousWord ||
    5066             :                  aAction == nsIEditor::eToBeginningOfLine) {
    5067             :         // Move to the end of the previous node
    5068           0 :         nsCOMPtr<nsIContent> priorNode = htmlEditor->GetPriorNode(blockParent,
    5069             :                                                                   offset,
    5070           0 :                                                                   true);
    5071           0 :         if (priorNode) {
    5072           0 :           EditorDOMPoint pt = GetGoodSelPointForNode(*priorNode, aAction);
    5073           0 :           nsresult rv = aSelection->Collapse(pt.node, pt.offset);
    5074           0 :           NS_ENSURE_SUCCESS(rv, rv);
    5075             :         } else {
    5076           0 :           nsresult rv = aSelection->Collapse(blockParent, offset + 1);
    5077           0 :           NS_ENSURE_SUCCESS(rv, rv);
    5078           0 :         }
    5079           0 :       } else if (aAction != nsIEditor::eNone) {
    5080           0 :         NS_RUNTIMEABORT("CheckForEmptyBlock doesn't support this action yet");
    5081             :       }
    5082             :     }
    5083           0 :     NS_ENSURE_STATE(htmlEditor);
    5084           0 :     *aHandled = true;
    5085           0 :     nsresult rv = htmlEditor->DeleteNode(emptyBlock);
    5086           0 :     NS_ENSURE_SUCCESS(rv, rv);
    5087             :   }
    5088           0 :   return NS_OK;
    5089             : }
    5090             : 
    5091             : Element*
    5092           0 : HTMLEditRules::CheckForInvisibleBR(Element& aBlock,
    5093             :                                    BRLocation aWhere,
    5094             :                                    int32_t aOffset)
    5095             : {
    5096           0 :   nsCOMPtr<nsINode> testNode;
    5097           0 :   int32_t testOffset = 0;
    5098             : 
    5099           0 :   if (aWhere == BRLocation::blockEnd) {
    5100             :     // No block crossing
    5101             :     nsCOMPtr<nsIContent> rightmostNode =
    5102           0 :       mHTMLEditor->GetRightmostChild(&aBlock, true);
    5103             : 
    5104           0 :     if (!rightmostNode) {
    5105           0 :       return nullptr;
    5106             :     }
    5107             : 
    5108           0 :     testNode = rightmostNode->GetParentNode();
    5109             :     // Use offset + 1, so last node is included in our evaluation
    5110           0 :     testOffset = testNode->IndexOf(rightmostNode) + 1;
    5111           0 :   } else if (aOffset) {
    5112           0 :     testNode = &aBlock;
    5113             :     // We'll check everything to the left of the input position
    5114           0 :     testOffset = aOffset;
    5115             :   } else {
    5116           0 :     return nullptr;
    5117             :   }
    5118             : 
    5119           0 :   WSRunObject wsTester(mHTMLEditor, testNode, testOffset);
    5120           0 :   if (WSType::br == wsTester.mStartReason) {
    5121           0 :     return wsTester.mStartReasonNode->AsElement();
    5122             :   }
    5123             : 
    5124           0 :   return nullptr;
    5125             : }
    5126             : 
    5127             : /**
    5128             :  * aLists and aTables allow the caller to specify what kind of content to
    5129             :  * "look inside".  If aTables is Tables::yes, look inside any table content,
    5130             :  * and insert the inner content into the supplied nsTArray at offset
    5131             :  * aIndex.  Similarly with aLists and list content.  aIndex is updated to
    5132             :  * point past inserted elements.
    5133             :  */
    5134             : void
    5135           0 : HTMLEditRules::GetInnerContent(
    5136             :                  nsINode& aNode,
    5137             :                  nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
    5138             :                  int32_t* aIndex,
    5139             :                  Lists aLists,
    5140             :                  Tables aTables)
    5141             : {
    5142           0 :   MOZ_ASSERT(aIndex);
    5143             : 
    5144           0 :   for (nsCOMPtr<nsIContent> node = mHTMLEditor->GetFirstEditableChild(aNode);
    5145           0 :        node; node = node->GetNextSibling()) {
    5146           0 :     if ((aLists == Lists::yes && (HTMLEditUtils::IsList(node) ||
    5147           0 :                                   HTMLEditUtils::IsListItem(node))) ||
    5148           0 :         (aTables == Tables::yes && HTMLEditUtils::IsTableElement(node))) {
    5149           0 :       GetInnerContent(*node, aOutArrayOfNodes, aIndex, aLists, aTables);
    5150             :     } else {
    5151           0 :       aOutArrayOfNodes.InsertElementAt(*aIndex, *node);
    5152           0 :       (*aIndex)++;
    5153             :     }
    5154             :   }
    5155           0 : }
    5156             : 
    5157             : /**
    5158             :  * Promotes selection to include blocks that have all their children selected.
    5159             :  */
    5160             : nsresult
    5161           0 : HTMLEditRules::ExpandSelectionForDeletion(Selection& aSelection)
    5162             : {
    5163             :   // Don't need to touch collapsed selections
    5164           0 :   if (aSelection.Collapsed()) {
    5165           0 :     return NS_OK;
    5166             :   }
    5167             : 
    5168             :   // We don't need to mess with cell selections, and we assume multirange
    5169             :   // selections are those.
    5170           0 :   if (aSelection.RangeCount() != 1) {
    5171           0 :     return NS_OK;
    5172             :   }
    5173             : 
    5174             :   // Find current sel start and end
    5175           0 :   NS_ENSURE_TRUE(aSelection.GetRangeAt(0), NS_ERROR_NULL_POINTER);
    5176           0 :   OwningNonNull<nsRange> range = *aSelection.GetRangeAt(0);
    5177             : 
    5178           0 :   nsCOMPtr<nsINode> selStartNode = range->GetStartContainer();
    5179           0 :   int32_t selStartOffset = range->StartOffset();
    5180           0 :   nsCOMPtr<nsINode> selEndNode = range->GetEndContainer();
    5181           0 :   int32_t selEndOffset = range->EndOffset();
    5182             : 
    5183             :   // Find current selection common block parent
    5184             :   nsCOMPtr<Element> selCommon =
    5185           0 :     HTMLEditor::GetBlock(*range->GetCommonAncestor());
    5186           0 :   NS_ENSURE_STATE(selCommon);
    5187             : 
    5188             :   // Set up for loops and cache our root element
    5189           0 :   nsCOMPtr<nsINode> firstBRParent;
    5190           0 :   nsCOMPtr<nsINode> unused;
    5191           0 :   int32_t visOffset = 0, firstBROffset = 0;
    5192           0 :   WSType wsType;
    5193           0 :   nsCOMPtr<Element> root = mHTMLEditor->GetActiveEditingHost();
    5194           0 :   NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
    5195             : 
    5196             :   // Find previous visible thingy before start of selection
    5197           0 :   if (selStartNode != selCommon && selStartNode != root) {
    5198             :     while (true) {
    5199           0 :       WSRunObject wsObj(mHTMLEditor, selStartNode, selStartOffset);
    5200           0 :       wsObj.PriorVisibleNode(selStartNode, selStartOffset, address_of(unused),
    5201           0 :                              &visOffset, &wsType);
    5202           0 :       if (wsType != WSType::thisBlock) {
    5203           0 :         break;
    5204             :       }
    5205             :       // We want to keep looking up.  But stop if we are crossing table
    5206             :       // element boundaries, or if we hit the root.
    5207           0 :       if (HTMLEditUtils::IsTableElement(wsObj.mStartReasonNode) ||
    5208           0 :           selCommon == wsObj.mStartReasonNode ||
    5209           0 :           root == wsObj.mStartReasonNode) {
    5210           0 :         break;
    5211             :       }
    5212           0 :       selStartNode = wsObj.mStartReasonNode->GetParentNode();
    5213           0 :       selStartOffset = selStartNode ?
    5214           0 :         selStartNode->IndexOf(wsObj.mStartReasonNode) : -1;
    5215           0 :     }
    5216             :   }
    5217             : 
    5218             :   // Find next visible thingy after end of selection
    5219           0 :   if (selEndNode != selCommon && selEndNode != root) {
    5220             :     for (;;) {
    5221           0 :       WSRunObject wsObj(mHTMLEditor, selEndNode, selEndOffset);
    5222           0 :       wsObj.NextVisibleNode(selEndNode, selEndOffset, address_of(unused),
    5223           0 :                             &visOffset, &wsType);
    5224           0 :       if (wsType == WSType::br) {
    5225           0 :         if (mHTMLEditor->IsVisBreak(wsObj.mEndReasonNode)) {
    5226           0 :           break;
    5227             :         }
    5228           0 :         if (!firstBRParent) {
    5229           0 :           firstBRParent = selEndNode;
    5230           0 :           firstBROffset = selEndOffset;
    5231             :         }
    5232           0 :         selEndNode = wsObj.mEndReasonNode->GetParentNode();
    5233           0 :         selEndOffset = selEndNode
    5234           0 :           ? selEndNode->IndexOf(wsObj.mEndReasonNode) + 1 : 0;
    5235           0 :       } else if (wsType == WSType::thisBlock) {
    5236             :         // We want to keep looking up.  But stop if we are crossing table
    5237             :         // element boundaries, or if we hit the root.
    5238           0 :         if (HTMLEditUtils::IsTableElement(wsObj.mEndReasonNode) ||
    5239           0 :             selCommon == wsObj.mEndReasonNode ||
    5240           0 :             root == wsObj.mEndReasonNode) {
    5241           0 :           break;
    5242             :         }
    5243           0 :         selEndNode = wsObj.mEndReasonNode->GetParentNode();
    5244           0 :         selEndOffset = 1 + selEndNode->IndexOf(wsObj.mEndReasonNode);
    5245             :       } else {
    5246           0 :         break;
    5247             :       }
    5248           0 :     }
    5249             :   }
    5250             :   // Now set the selection to the new range
    5251           0 :   aSelection.Collapse(selStartNode, selStartOffset);
    5252             : 
    5253             :   // Expand selection endpoint only if we didn't pass a br, or if we really
    5254             :   // needed to pass that br (i.e., its block is now totally selected)
    5255           0 :   bool doEndExpansion = true;
    5256           0 :   if (firstBRParent) {
    5257             :     // Find block node containing br
    5258           0 :     nsCOMPtr<Element> brBlock = HTMLEditor::GetBlock(*firstBRParent);
    5259           0 :     bool nodeBefore = false, nodeAfter = false;
    5260             : 
    5261             :     // Create a range that represents expanded selection
    5262           0 :     RefPtr<nsRange> range = new nsRange(selStartNode);
    5263           0 :     nsresult rv = range->SetStartAndEnd(selStartNode, selStartOffset,
    5264           0 :                                         selEndNode, selEndOffset);
    5265           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    5266           0 :       return rv;
    5267             :     }
    5268             : 
    5269             :     // Check if block is entirely inside range
    5270           0 :     if (brBlock) {
    5271           0 :       nsRange::CompareNodeToRange(brBlock, range, &nodeBefore, &nodeAfter);
    5272             :     }
    5273             : 
    5274             :     // If block isn't contained, forgo grabbing the br in expanded selection
    5275           0 :     if (nodeBefore || nodeAfter) {
    5276           0 :       doEndExpansion = false;
    5277             :     }
    5278             :   }
    5279           0 :   if (doEndExpansion) {
    5280           0 :     nsresult rv = aSelection.Extend(selEndNode, selEndOffset);
    5281           0 :     NS_ENSURE_SUCCESS(rv, rv);
    5282             :   } else {
    5283             :     // Only expand to just before br
    5284           0 :     nsresult rv = aSelection.Extend(firstBRParent, firstBROffset);
    5285           0 :     NS_ENSURE_SUCCESS(rv, rv);
    5286             :   }
    5287             : 
    5288           0 :   return NS_OK;
    5289             : }
    5290             : 
    5291             : /**
    5292             :  * NormalizeSelection() tweaks non-collapsed selections to be more "natural".
    5293             :  * Idea here is to adjust selection endpoint so that they do not cross breaks
    5294             :  * or block boundaries unless something editable beyond that boundary is also
    5295             :  * selected.  This adjustment makes it much easier for the various block
    5296             :  * operations to determine what nodes to act on.
    5297             :  */
    5298             : nsresult
    5299           0 : HTMLEditRules::NormalizeSelection(Selection* inSelection)
    5300             : {
    5301           0 :   NS_ENSURE_TRUE(inSelection, NS_ERROR_NULL_POINTER);
    5302             : 
    5303             :   // don't need to touch collapsed selections
    5304           0 :   if (inSelection->Collapsed()) {
    5305           0 :     return NS_OK;
    5306             :   }
    5307             : 
    5308             :   int32_t rangeCount;
    5309           0 :   nsresult rv = inSelection->GetRangeCount(&rangeCount);
    5310           0 :   NS_ENSURE_SUCCESS(rv, rv);
    5311             : 
    5312             :   // we don't need to mess with cell selections, and we assume multirange selections are those.
    5313           0 :   if (rangeCount != 1) {
    5314           0 :     return NS_OK;
    5315             :   }
    5316             : 
    5317           0 :   RefPtr<nsRange> range = inSelection->GetRangeAt(0);
    5318           0 :   NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
    5319           0 :   nsCOMPtr<nsIDOMNode> startNode, endNode;
    5320             :   int32_t startOffset, endOffset;
    5321           0 :   nsCOMPtr<nsIDOMNode> newStartNode, newEndNode;
    5322             :   int32_t newStartOffset, newEndOffset;
    5323             : 
    5324           0 :   rv = range->GetStartContainer(getter_AddRefs(startNode));
    5325           0 :   NS_ENSURE_SUCCESS(rv, rv);
    5326           0 :   rv = range->GetStartOffset(&startOffset);
    5327           0 :   NS_ENSURE_SUCCESS(rv, rv);
    5328           0 :   rv = range->GetEndContainer(getter_AddRefs(endNode));
    5329           0 :   NS_ENSURE_SUCCESS(rv, rv);
    5330           0 :   rv = range->GetEndOffset(&endOffset);
    5331           0 :   NS_ENSURE_SUCCESS(rv, rv);
    5332             : 
    5333             :   // adjusted values default to original values
    5334           0 :   newStartNode = startNode;
    5335           0 :   newStartOffset = startOffset;
    5336           0 :   newEndNode = endNode;
    5337           0 :   newEndOffset = endOffset;
    5338             : 
    5339             :   // some locals we need for whitespace code
    5340           0 :   nsCOMPtr<nsINode> unused;
    5341             :   int32_t offset;
    5342           0 :   WSType wsType;
    5343             : 
    5344             :   // let the whitespace code do the heavy lifting
    5345           0 :   WSRunObject wsEndObj(mHTMLEditor, endNode, endOffset);
    5346             :   // is there any intervening visible whitespace?  if so we can't push selection past that,
    5347             :   // it would visibly change maening of users selection
    5348           0 :   nsCOMPtr<nsINode> endNode_(do_QueryInterface(endNode));
    5349           0 :   wsEndObj.PriorVisibleNode(endNode_, endOffset, address_of(unused),
    5350           0 :                             &offset, &wsType);
    5351           0 :   if (wsType != WSType::text && wsType != WSType::normalWS) {
    5352             :     // eThisBlock and eOtherBlock conveniently distinquish cases
    5353             :     // of going "down" into a block and "up" out of a block.
    5354           0 :     if (wsEndObj.mStartReason == WSType::otherBlock) {
    5355             :       // endpoint is just after the close of a block.
    5356             :       nsCOMPtr<nsIDOMNode> child =
    5357           0 :         GetAsDOMNode(mHTMLEditor->GetRightmostChild(wsEndObj.mStartReasonNode,
    5358           0 :                                                     true));
    5359           0 :       if (child) {
    5360           0 :         newEndNode = EditorBase::GetNodeLocation(child, &newEndOffset);
    5361           0 :         ++newEndOffset; // offset *after* child
    5362             :       }
    5363             :       // else block is empty - we can leave selection alone here, i think.
    5364           0 :     } else if (wsEndObj.mStartReason == WSType::thisBlock) {
    5365             :       // endpoint is just after start of this block
    5366           0 :       nsCOMPtr<nsIDOMNode> child;
    5367           0 :       NS_ENSURE_STATE(mHTMLEditor);
    5368           0 :       mHTMLEditor->GetPriorHTMLNode(endNode, endOffset, address_of(child));
    5369           0 :       if (child) {
    5370           0 :         newEndNode = EditorBase::GetNodeLocation(child, &newEndOffset);
    5371           0 :         ++newEndOffset; // offset *after* child
    5372             :       }
    5373             :       // else block is empty - we can leave selection alone here, i think.
    5374           0 :     } else if (wsEndObj.mStartReason == WSType::br) {
    5375             :       // endpoint is just after break.  lets adjust it to before it.
    5376             :       newEndNode =
    5377           0 :         EditorBase::GetNodeLocation(GetAsDOMNode(wsEndObj.mStartReasonNode),
    5378           0 :                                     &newEndOffset);
    5379             :     }
    5380             :   }
    5381             : 
    5382             : 
    5383             :   // similar dealio for start of range
    5384           0 :   WSRunObject wsStartObj(mHTMLEditor, startNode, startOffset);
    5385             :   // is there any intervening visible whitespace?  if so we can't push selection past that,
    5386             :   // it would visibly change maening of users selection
    5387           0 :   nsCOMPtr<nsINode> startNode_(do_QueryInterface(startNode));
    5388           0 :   wsStartObj.NextVisibleNode(startNode_, startOffset, address_of(unused),
    5389           0 :                              &offset, &wsType);
    5390           0 :   if (wsType != WSType::text && wsType != WSType::normalWS) {
    5391             :     // eThisBlock and eOtherBlock conveniently distinquish cases
    5392             :     // of going "down" into a block and "up" out of a block.
    5393           0 :     if (wsStartObj.mEndReason == WSType::otherBlock) {
    5394             :       // startpoint is just before the start of a block.
    5395             :       nsCOMPtr<nsIDOMNode> child =
    5396           0 :         GetAsDOMNode(mHTMLEditor->GetLeftmostChild(wsStartObj.mEndReasonNode,
    5397           0 :                                                    true));
    5398           0 :       if (child) {
    5399           0 :         newStartNode = EditorBase::GetNodeLocation(child, &newStartOffset);
    5400             :       }
    5401             :       // else block is empty - we can leave selection alone here, i think.
    5402           0 :     } else if (wsStartObj.mEndReason == WSType::thisBlock) {
    5403             :       // startpoint is just before end of this block
    5404           0 :       nsCOMPtr<nsIDOMNode> child;
    5405           0 :       NS_ENSURE_STATE(mHTMLEditor);
    5406           0 :       mHTMLEditor->GetNextHTMLNode(startNode, startOffset, address_of(child));
    5407           0 :       if (child) {
    5408           0 :         newStartNode = EditorBase::GetNodeLocation(child, &newStartOffset);
    5409             :       }
    5410             :       // else block is empty - we can leave selection alone here, i think.
    5411           0 :     } else if (wsStartObj.mEndReason == WSType::br) {
    5412             :       // startpoint is just before a break.  lets adjust it to after it.
    5413             :       newStartNode =
    5414           0 :         EditorBase::GetNodeLocation(GetAsDOMNode(wsStartObj.mEndReasonNode),
    5415           0 :                                     &newStartOffset);
    5416           0 :       ++newStartOffset; // offset *after* break
    5417             :     }
    5418             :   }
    5419             : 
    5420             :   // there is a demented possiblity we have to check for.  We might have a very strange selection
    5421             :   // that is not collapsed and yet does not contain any editable content, and satisfies some of the
    5422             :   // above conditions that cause tweaking.  In this case we don't want to tweak the selection into
    5423             :   // a block it was never in, etc.  There are a variety of strategies one might use to try to
    5424             :   // detect these cases, but I think the most straightforward is to see if the adjusted locations
    5425             :   // "cross" the old values: ie, new end before old start, or new start after old end.  If so
    5426             :   // then just leave things alone.
    5427             : 
    5428             :   int16_t comp;
    5429           0 :   comp = nsContentUtils::ComparePoints(startNode, startOffset,
    5430             :                                        newEndNode, newEndOffset);
    5431           0 :   if (comp == 1) {
    5432           0 :     return NS_OK;  // New end before old start.
    5433             :   }
    5434           0 :   comp = nsContentUtils::ComparePoints(newStartNode, newStartOffset,
    5435             :                                        endNode, endOffset);
    5436           0 :   if (comp == 1) {
    5437           0 :     return NS_OK;  // New start after old end.
    5438             :   }
    5439             : 
    5440             :   // otherwise set selection to new values.
    5441           0 :   inSelection->Collapse(newStartNode, newStartOffset);
    5442           0 :   inSelection->Extend(newEndNode, newEndOffset);
    5443           0 :   return NS_OK;
    5444             : }
    5445             : 
    5446             : /**
    5447             :  * GetPromotedPoint() figures out where a start or end point for a block
    5448             :  * operation really is.
    5449             :  */
    5450             : EditorDOMPoint
    5451           0 : HTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere,
    5452             :                                 nsINode& aNode,
    5453             :                                 int32_t aOffset,
    5454             :                                 EditAction actionID)
    5455             : {
    5456           0 :   if (NS_WARN_IF(!mHTMLEditor)) {
    5457           0 :     return EditorDOMPoint(&aNode, aOffset);
    5458             :   }
    5459           0 :   RefPtr<HTMLEditor> htmlEditor = mHTMLEditor;
    5460             : 
    5461             :   // we do one thing for text actions, something else entirely for other
    5462             :   // actions
    5463           0 :   if (actionID == EditAction::insertText ||
    5464           0 :       actionID == EditAction::insertIMEText ||
    5465           0 :       actionID == EditAction::insertBreak ||
    5466             :       actionID == EditAction::deleteText) {
    5467             :     bool isSpace, isNBSP;
    5468             :     nsCOMPtr<nsIContent> content =
    5469           0 :       aNode.IsContent() ? aNode.AsContent() : nullptr;
    5470           0 :     nsCOMPtr<nsIContent> temp;
    5471           0 :     int32_t newOffset = aOffset;
    5472             :     // for text actions, we want to look backwards (or forwards, as
    5473             :     // appropriate) for additional whitespace or nbsp's.  We may have to act on
    5474             :     // these later even though they are outside of the initial selection.  Even
    5475             :     // if they are in another node!
    5476           0 :     while (content) {
    5477             :       int32_t offset;
    5478           0 :       if (aWhere == kStart) {
    5479           0 :         htmlEditor->IsPrevCharInNodeWhitespace(content, newOffset,
    5480             :                                                &isSpace, &isNBSP,
    5481           0 :                                                getter_AddRefs(temp), &offset);
    5482             :       } else {
    5483           0 :         htmlEditor->IsNextCharInNodeWhitespace(content, newOffset,
    5484             :                                                &isSpace, &isNBSP,
    5485           0 :                                                getter_AddRefs(temp), &offset);
    5486             :       }
    5487           0 :       if (isSpace || isNBSP) {
    5488           0 :         content = temp;
    5489           0 :         newOffset = offset;
    5490             :       } else {
    5491             :         break;
    5492             :       }
    5493             :     }
    5494             : 
    5495           0 :     return EditorDOMPoint(content, newOffset);
    5496             :   }
    5497             : 
    5498           0 :   nsCOMPtr<nsINode> node = &aNode;
    5499           0 :   int32_t offset = aOffset;
    5500             : 
    5501             :   // else not a text section.  In this case we want to see if we should grab
    5502             :   // any adjacent inline nodes and/or parents and other ancestors
    5503           0 :   if (aWhere == kStart) {
    5504             :     // some special casing for text nodes
    5505           0 :     if (node->IsNodeOfType(nsINode::eTEXT)) {
    5506           0 :       if (!node->GetParentNode()) {
    5507             :         // Okay, can't promote any further
    5508           0 :         return EditorDOMPoint(node, offset);
    5509             :       }
    5510           0 :       offset = node->GetParentNode()->IndexOf(node);
    5511           0 :       node = node->GetParentNode();
    5512             :     }
    5513             : 
    5514             :     // look back through any further inline nodes that aren't across a <br>
    5515             :     // from us, and that are enclosed in the same block.
    5516             :     nsCOMPtr<nsINode> priorNode =
    5517           0 :       htmlEditor->GetPriorHTMLNode(node, offset, true);
    5518             : 
    5519           0 :     while (priorNode && priorNode->GetParentNode() &&
    5520           0 :            !htmlEditor->IsVisBreak(priorNode) &&
    5521           0 :            !IsBlockNode(*priorNode)) {
    5522           0 :       offset = priorNode->GetParentNode()->IndexOf(priorNode);
    5523           0 :       node = priorNode->GetParentNode();
    5524           0 :       priorNode = htmlEditor->GetPriorHTMLNode(node, offset, true);
    5525             :     }
    5526             : 
    5527             :     // finding the real start for this point.  look up the tree for as long as
    5528             :     // we are the first node in the container, and as long as we haven't hit
    5529             :     // the body node.
    5530             :     nsCOMPtr<nsIContent> nearNode =
    5531           0 :       htmlEditor->GetPriorHTMLNode(node, offset, true);
    5532           0 :     while (!nearNode && !node->IsHTMLElement(nsGkAtoms::body) &&
    5533           0 :            node->GetParentNode()) {
    5534             :       // some cutoffs are here: we don't need to also include them in the
    5535             :       // aWhere == kEnd case.  as long as they are in one or the other it will
    5536             :       // work.  special case for outdent: don't keep looking up if we have
    5537             :       // found a blockquote element to act on
    5538           0 :       if (actionID == EditAction::outdent &&
    5539           0 :           node->IsHTMLElement(nsGkAtoms::blockquote)) {
    5540           0 :         break;
    5541             :       }
    5542             : 
    5543           0 :       int32_t parentOffset = node->GetParentNode()->IndexOf(node);
    5544           0 :       nsCOMPtr<nsINode> parent = node->GetParentNode();
    5545             : 
    5546             :       // Don't walk past the editable section. Note that we need to check
    5547             :       // before walking up to a parent because we need to return the parent
    5548             :       // object, so the parent itself might not be in the editable area, but
    5549             :       // it's OK if we're not performing a block-level action.
    5550           0 :       bool blockLevelAction = actionID == EditAction::indent ||
    5551           0 :                               actionID == EditAction::outdent ||
    5552           0 :                               actionID == EditAction::align ||
    5553           0 :                               actionID == EditAction::makeBasicBlock;
    5554           0 :       if (!htmlEditor->IsDescendantOfEditorRoot(parent) &&
    5555           0 :           (blockLevelAction ||
    5556           0 :            !htmlEditor->IsDescendantOfEditorRoot(node))) {
    5557           0 :         break;
    5558             :       }
    5559             : 
    5560           0 :       node = parent;
    5561           0 :       offset = parentOffset;
    5562           0 :       nearNode = htmlEditor->GetPriorHTMLNode(node, offset, true);
    5563             :     }
    5564           0 :     return EditorDOMPoint(node, offset);
    5565             :   }
    5566             : 
    5567             :   // aWhere == kEnd
    5568             :   // some special casing for text nodes
    5569           0 :   if (node->IsNodeOfType(nsINode::eTEXT)) {
    5570           0 :     if (!node->GetParentNode()) {
    5571             :       // Okay, can't promote any further
    5572           0 :       return EditorDOMPoint(node, offset);
    5573             :     }
    5574             :     // want to be after the text node
    5575           0 :     offset = 1 + node->GetParentNode()->IndexOf(node);
    5576           0 :     node = node->GetParentNode();
    5577             :   }
    5578             : 
    5579             :   // look ahead through any further inline nodes that aren't across a <br> from
    5580             :   // us, and that are enclosed in the same block.
    5581             :   nsCOMPtr<nsIContent> nextNode =
    5582           0 :     htmlEditor->GetNextHTMLNode(node, offset, true);
    5583             : 
    5584           0 :   while (nextNode && !IsBlockNode(*nextNode) && nextNode->GetParentNode()) {
    5585           0 :     offset = 1 + nextNode->GetParentNode()->IndexOf(nextNode);
    5586           0 :     node = nextNode->GetParentNode();
    5587           0 :     if (htmlEditor->IsVisBreak(nextNode)) {
    5588           0 :       break;
    5589             :     }
    5590             : 
    5591             :     // Check for newlines in pre-formatted text nodes.
    5592             :     bool isPRE;
    5593           0 :     htmlEditor->IsPreformatted(nextNode->AsDOMNode(), &isPRE);
    5594           0 :     if (isPRE) {
    5595           0 :       if (EditorBase::IsTextNode(nextNode)) {
    5596           0 :         nsAutoString tempString;
    5597           0 :         nextNode->GetAsText()->GetData(tempString);
    5598           0 :         int32_t newlinePos = tempString.FindChar(nsCRT::LF);
    5599           0 :         if (newlinePos >= 0) {
    5600           0 :           if (static_cast<uint32_t>(newlinePos) + 1 == tempString.Length()) {
    5601             :             // No need for special processing if the newline is at the end.
    5602           0 :             break;
    5603             :           }
    5604           0 :           return EditorDOMPoint(nextNode, newlinePos + 1);
    5605             :         }
    5606             :       }
    5607             :     }
    5608           0 :     nextNode = htmlEditor->GetNextHTMLNode(node, offset, true);
    5609             :   }
    5610             : 
    5611             :   // finding the real end for this point.  look up the tree for as long as we
    5612             :   // are the last node in the container, and as long as we haven't hit the body
    5613             :   // node.
    5614             :   nsCOMPtr<nsIContent> nearNode =
    5615           0 :     htmlEditor->GetNextHTMLNode(node, offset, true);
    5616           0 :   while (!nearNode && !node->IsHTMLElement(nsGkAtoms::body) &&
    5617           0 :          node->GetParentNode()) {
    5618           0 :     int32_t parentOffset = node->GetParentNode()->IndexOf(node);
    5619           0 :     nsCOMPtr<nsINode> parent = node->GetParentNode();
    5620             : 
    5621             :     // Don't walk past the editable section. Note that we need to check before
    5622             :     // walking up to a parent because we need to return the parent object, so
    5623             :     // the parent itself might not be in the editable area, but it's OK.
    5624           0 :     if (!htmlEditor->IsDescendantOfEditorRoot(node) &&
    5625           0 :         !htmlEditor->IsDescendantOfEditorRoot(parent)) {
    5626           0 :       break;
    5627             :     }
    5628             : 
    5629           0 :     node = parent;
    5630             :     // we want to be AFTER nearNode
    5631           0 :     offset = parentOffset + 1;
    5632           0 :     nearNode = htmlEditor->GetNextHTMLNode(node, offset, true);
    5633             :   }
    5634           0 :   return EditorDOMPoint(node, offset);
    5635             : }
    5636             : 
    5637             : /**
    5638             :  * GetPromotedRanges() runs all the selection range endpoint through
    5639             :  * GetPromotedPoint().
    5640             :  */
    5641             : void
    5642           0 : HTMLEditRules::GetPromotedRanges(Selection& aSelection,
    5643             :                                  nsTArray<RefPtr<nsRange>>& outArrayOfRanges,
    5644             :                                  EditAction inOperationType)
    5645             : {
    5646           0 :   uint32_t rangeCount = aSelection.RangeCount();
    5647             : 
    5648           0 :   for (uint32_t i = 0; i < rangeCount; i++) {
    5649           0 :     RefPtr<nsRange> selectionRange = aSelection.GetRangeAt(i);
    5650           0 :     MOZ_ASSERT(selectionRange);
    5651             : 
    5652             :     // Clone range so we don't muck with actual selection ranges
    5653           0 :     RefPtr<nsRange> opRange = selectionRange->CloneRange();
    5654             : 
    5655             :     // Make a new adjusted range to represent the appropriate block content.
    5656             :     // The basic idea is to push out the range endpoints to truly enclose the
    5657             :     // blocks that we will affect.  This call alters opRange.
    5658           0 :     PromoteRange(*opRange, inOperationType);
    5659             : 
    5660             :     // Stuff new opRange into array
    5661           0 :     outArrayOfRanges.AppendElement(opRange);
    5662             :   }
    5663           0 : }
    5664             : 
    5665             : /**
    5666             :  * PromoteRange() expands a range to include any parents for which all editable
    5667             :  * children are already in range.
    5668             :  */
    5669             : void
    5670           0 : HTMLEditRules::PromoteRange(nsRange& aRange,
    5671             :                             EditAction aOperationType)
    5672             : {
    5673           0 :   NS_ENSURE_TRUE(mHTMLEditor, );
    5674           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    5675             : 
    5676           0 :   if (!aRange.IsPositioned()) {
    5677           0 :     return;
    5678             :   }
    5679             : 
    5680           0 :   nsCOMPtr<nsINode> startNode = aRange.GetStartContainer();
    5681           0 :   nsCOMPtr<nsINode> endNode = aRange.GetEndContainer();
    5682           0 :   int32_t startOffset = aRange.StartOffset();
    5683           0 :   int32_t endOffset = aRange.EndOffset();
    5684             : 
    5685             :   // MOOSE major hack:
    5686             :   // GetPromotedPoint doesn't really do the right thing for collapsed ranges
    5687             :   // inside block elements that contain nothing but a solo <br>.  It's easier
    5688             :   // to put a workaround here than to revamp GetPromotedPoint.  :-(
    5689           0 :   if (startNode == endNode && startOffset == endOffset) {
    5690           0 :     nsCOMPtr<Element> block = htmlEditor->GetBlock(*startNode);
    5691           0 :     if (block) {
    5692           0 :       bool bIsEmptyNode = false;
    5693           0 :       nsCOMPtr<nsIContent> root = htmlEditor->GetActiveEditingHost();
    5694             :       // Make sure we don't go higher than our root element in the content tree
    5695           0 :       NS_ENSURE_TRUE(root, );
    5696           0 :       if (!nsContentUtils::ContentIsDescendantOf(root, block)) {
    5697           0 :         htmlEditor->IsEmptyNode(block, &bIsEmptyNode, true, false);
    5698             :       }
    5699           0 :       if (bIsEmptyNode) {
    5700           0 :         startNode = block;
    5701           0 :         endNode = block;
    5702           0 :         startOffset = 0;
    5703           0 :         endOffset = block->Length();
    5704             :       }
    5705             :     }
    5706             :   }
    5707             : 
    5708           0 :   if (aOperationType == EditAction::insertText ||
    5709           0 :       aOperationType == EditAction::insertIMEText ||
    5710           0 :       aOperationType == EditAction::insertBreak ||
    5711             :       aOperationType == EditAction::deleteText) {
    5712           0 :      if (!startNode->IsContent() ||
    5713           0 :          !endNode->IsContent()) {
    5714             :        // GetPromotedPoint cannot promote node when action type is text
    5715             :        // operation and selected node isn't content node.
    5716           0 :        return;
    5717             :      }
    5718             :   }
    5719             : 
    5720             :   // Make a new adjusted range to represent the appropriate block content.
    5721             :   // This is tricky.  The basic idea is to push out the range endpoints to
    5722             :   // truly enclose the blocks that we will affect.
    5723             : 
    5724             :   EditorDOMPoint opStart =
    5725           0 :     GetPromotedPoint(kStart, *startNode, startOffset, aOperationType);
    5726             :   EditorDOMPoint opEnd =
    5727           0 :     GetPromotedPoint(kEnd, *endNode, endOffset, aOperationType);
    5728             : 
    5729             :   // Make sure that the new range ends up to be in the editable section.
    5730           0 :   if (!htmlEditor->IsDescendantOfEditorRoot(
    5731           0 :         EditorBase::GetNodeAtRangeOffsetPoint(opStart.node, opStart.offset)) ||
    5732           0 :       !htmlEditor->IsDescendantOfEditorRoot(
    5733           0 :         EditorBase::GetNodeAtRangeOffsetPoint(opEnd.node, opEnd.offset - 1))) {
    5734           0 :     return;
    5735             :   }
    5736             : 
    5737             :   DebugOnly<nsresult> rv =
    5738           0 :     aRange.SetStartAndEnd(opStart.node, opStart.offset,
    5739           0 :                           opEnd.node, opEnd.offset);
    5740           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    5741             : }
    5742             : 
    5743             : class UniqueFunctor final : public BoolDomIterFunctor
    5744             : {
    5745             : public:
    5746           0 :   explicit UniqueFunctor(nsTArray<OwningNonNull<nsINode>>& aArray)
    5747           0 :     : mArray(aArray)
    5748             :   {
    5749           0 :   }
    5750             : 
    5751             :   // Used to build list of all nodes iterator covers.
    5752           0 :   virtual bool operator()(nsINode* aNode) const
    5753             :   {
    5754           0 :     return !mArray.Contains(aNode);
    5755             :   }
    5756             : 
    5757             : private:
    5758             :   nsTArray<OwningNonNull<nsINode>>& mArray;
    5759             : };
    5760             : 
    5761             : /**
    5762             :  * GetNodesForOperation() runs through the ranges in the array and construct a
    5763             :  * new array of nodes to be acted on.
    5764             :  */
    5765             : nsresult
    5766           0 : HTMLEditRules::GetNodesForOperation(
    5767             :                  nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
    5768             :                  nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
    5769             :                  EditAction aOperationType,
    5770             :                  TouchContent aTouchContent)
    5771             : {
    5772           0 :   NS_ENSURE_STATE(mHTMLEditor);
    5773           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    5774             : 
    5775           0 :   int32_t rangeCount = aArrayOfRanges.Length();
    5776           0 :   if (aTouchContent == TouchContent::yes) {
    5777             :     // Split text nodes. This is necessary, since GetPromotedPoint() may return a
    5778             :     // range ending in a text node in case where part of a pre-formatted
    5779             :     // elements needs to be moved.
    5780           0 :     for (int32_t i = 0; i < rangeCount; i++) {
    5781           0 :       RefPtr<nsRange> r = aArrayOfRanges[i];
    5782           0 :       nsCOMPtr<nsINode> endContainer = r->GetEndContainer();
    5783           0 :       if (!endContainer->IsNodeOfType(nsINode::eTEXT)) {
    5784           0 :         continue;
    5785             :       }
    5786           0 :       int32_t offset = r->EndOffset();
    5787             : 
    5788           0 :       if (0 < offset &&
    5789           0 :           offset < static_cast<int32_t>(endContainer->Length())) {
    5790             :         // Split the text node.
    5791           0 :         nsCOMPtr<nsIDOMNode> tempNode;
    5792           0 :         nsresult rv = htmlEditor->SplitNode(endContainer->AsDOMNode(), offset,
    5793           0 :                                             getter_AddRefs(tempNode));
    5794           0 :         NS_ENSURE_SUCCESS(rv, rv);
    5795             : 
    5796             :         // Correct the range.
    5797             :         // The new end parent becomes the parent node of the text.
    5798           0 :         nsCOMPtr<nsIContent> newParent = endContainer->GetParent();
    5799           0 :         r->SetEnd(newParent, newParent->IndexOf(endContainer));
    5800             :       }
    5801             :     }
    5802             :   }
    5803             : 
    5804             :   // Bust up any inlines that cross our range endpoints, but only if we are
    5805             :   // allowed to touch content.
    5806             : 
    5807           0 :   if (aTouchContent == TouchContent::yes) {
    5808           0 :     nsTArray<OwningNonNull<RangeItem>> rangeItemArray;
    5809           0 :     rangeItemArray.AppendElements(rangeCount);
    5810             : 
    5811             :     // First register ranges for special editor gravity
    5812           0 :     for (int32_t i = 0; i < rangeCount; i++) {
    5813           0 :       rangeItemArray[i] = new RangeItem();
    5814           0 :       rangeItemArray[i]->StoreRange(aArrayOfRanges[0]);
    5815           0 :       htmlEditor->mRangeUpdater.RegisterRangeItem(rangeItemArray[i]);
    5816           0 :       aArrayOfRanges.RemoveElementAt(0);
    5817             :     }
    5818             :     // Now bust up inlines.
    5819           0 :     nsresult rv = NS_OK;
    5820           0 :     for (auto& item : Reversed(rangeItemArray)) {
    5821           0 :       rv = BustUpInlinesAtRangeEndpoints(*item);
    5822           0 :       if (NS_FAILED(rv)) {
    5823           0 :         break;
    5824             :       }
    5825             :     }
    5826             :     // Then unregister the ranges
    5827           0 :     for (auto& item : rangeItemArray) {
    5828           0 :       htmlEditor->mRangeUpdater.DropRangeItem(item);
    5829           0 :       aArrayOfRanges.AppendElement(item->GetRange());
    5830             :     }
    5831           0 :     NS_ENSURE_SUCCESS(rv, rv);
    5832             :   }
    5833             :   // Gather up a list of all the nodes
    5834           0 :   for (auto& range : aArrayOfRanges) {
    5835           0 :     DOMSubtreeIterator iter;
    5836           0 :     nsresult rv = iter.Init(*range);
    5837           0 :     NS_ENSURE_SUCCESS(rv, rv);
    5838           0 :     if (aOutArrayOfNodes.IsEmpty()) {
    5839           0 :       iter.AppendList(TrivialFunctor(), aOutArrayOfNodes);
    5840             :     } else {
    5841             :       // We don't want duplicates in aOutArrayOfNodes, so we use an
    5842             :       // iterator/functor that only return nodes that are not already in
    5843             :       // aOutArrayOfNodes.
    5844           0 :       nsTArray<OwningNonNull<nsINode>> nodes;
    5845           0 :       iter.AppendList(UniqueFunctor(aOutArrayOfNodes), nodes);
    5846           0 :       aOutArrayOfNodes.AppendElements(nodes);
    5847             :     }
    5848             :   }
    5849             : 
    5850             :   // Certain operations should not act on li's and td's, but rather inside
    5851             :   // them.  Alter the list as needed.
    5852           0 :   if (aOperationType == EditAction::makeBasicBlock) {
    5853           0 :     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
    5854           0 :       OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
    5855           0 :       if (HTMLEditUtils::IsListItem(node)) {
    5856           0 :         int32_t j = i;
    5857           0 :         aOutArrayOfNodes.RemoveElementAt(i);
    5858           0 :         GetInnerContent(*node, aOutArrayOfNodes, &j);
    5859             :       }
    5860             :     }
    5861             :     // Empty text node shouldn't be selected if unnecessary
    5862           0 :     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
    5863           0 :       OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
    5864           0 :       if (EditorBase::IsTextNode(node)) {
    5865             :         // Don't select empty text except to empty block
    5866           0 :         bool isEmpty = false;
    5867           0 :         htmlEditor->IsVisTextNode(node->AsContent(), &isEmpty, false);
    5868           0 :         if (isEmpty) {
    5869           0 :           aOutArrayOfNodes.RemoveElementAt(i);
    5870             :         }
    5871             :       }
    5872             :     }
    5873             :   }
    5874             :   // Indent/outdent already do something special for list items, but we still
    5875             :   // need to make sure we don't act on table elements
    5876           0 :   else if (aOperationType == EditAction::outdent ||
    5877           0 :            aOperationType == EditAction::indent ||
    5878             :            aOperationType == EditAction::setAbsolutePosition) {
    5879           0 :     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
    5880           0 :       OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
    5881           0 :       if (HTMLEditUtils::IsTableElementButNotTable(node)) {
    5882           0 :         int32_t j = i;
    5883           0 :         aOutArrayOfNodes.RemoveElementAt(i);
    5884           0 :         GetInnerContent(*node, aOutArrayOfNodes, &j);
    5885             :       }
    5886             :     }
    5887             :   }
    5888             :   // Outdent should look inside of divs.
    5889           0 :   if (aOperationType == EditAction::outdent &&
    5890           0 :       !htmlEditor->IsCSSEnabled()) {
    5891           0 :     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
    5892           0 :       OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
    5893           0 :       if (node->IsHTMLElement(nsGkAtoms::div)) {
    5894           0 :         int32_t j = i;
    5895           0 :         aOutArrayOfNodes.RemoveElementAt(i);
    5896           0 :         GetInnerContent(*node, aOutArrayOfNodes, &j, Lists::no, Tables::no);
    5897             :       }
    5898             :     }
    5899             :   }
    5900             : 
    5901             : 
    5902             :   // Post-process the list to break up inline containers that contain br's, but
    5903             :   // only for operations that might care, like making lists or paragraphs
    5904           0 :   if (aOperationType == EditAction::makeBasicBlock ||
    5905           0 :       aOperationType == EditAction::makeList ||
    5906           0 :       aOperationType == EditAction::align ||
    5907           0 :       aOperationType == EditAction::setAbsolutePosition ||
    5908           0 :       aOperationType == EditAction::indent ||
    5909             :       aOperationType == EditAction::outdent) {
    5910           0 :     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
    5911           0 :       OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
    5912           0 :       if (aTouchContent == TouchContent::yes && IsInlineNode(node) &&
    5913           0 :           htmlEditor->IsContainer(node) && !EditorBase::IsTextNode(node)) {
    5914           0 :         nsTArray<OwningNonNull<nsINode>> arrayOfInlines;
    5915           0 :         nsresult rv = BustUpInlinesAtBRs(*node->AsContent(), arrayOfInlines);
    5916           0 :         NS_ENSURE_SUCCESS(rv, rv);
    5917             : 
    5918             :         // Put these nodes in aOutArrayOfNodes, replacing the current node
    5919           0 :         aOutArrayOfNodes.RemoveElementAt(i);
    5920           0 :         aOutArrayOfNodes.InsertElementsAt(i, arrayOfInlines);
    5921             :       }
    5922             :     }
    5923             :   }
    5924           0 :   return NS_OK;
    5925             : }
    5926             : 
    5927             : void
    5928           0 : HTMLEditRules::GetChildNodesForOperation(
    5929             :                  nsINode& aNode,
    5930             :                  nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes)
    5931             : {
    5932           0 :   for (nsCOMPtr<nsIContent> child = aNode.GetFirstChild();
    5933           0 :        child; child = child->GetNextSibling()) {
    5934           0 :     outArrayOfNodes.AppendElement(*child);
    5935             :   }
    5936           0 : }
    5937             : 
    5938             : nsresult
    5939           0 : HTMLEditRules::GetListActionNodes(
    5940             :                  nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
    5941             :                  EntireList aEntireList,
    5942             :                  TouchContent aTouchContent)
    5943             : {
    5944           0 :   NS_ENSURE_STATE(mHTMLEditor);
    5945           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    5946             : 
    5947           0 :   RefPtr<Selection> selection = htmlEditor->GetSelection();
    5948           0 :   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
    5949             : 
    5950             :   // Added this in so that ui code can ask to change an entire list, even if
    5951             :   // selection is only in part of it.  used by list item dialog.
    5952           0 :   if (aEntireList == EntireList::yes) {
    5953           0 :     uint32_t rangeCount = selection->RangeCount();
    5954           0 :     for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
    5955           0 :       RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
    5956           0 :       for (nsCOMPtr<nsINode> parent = range->GetCommonAncestor();
    5957           0 :            parent; parent = parent->GetParentNode()) {
    5958           0 :         if (HTMLEditUtils::IsList(parent)) {
    5959           0 :           aOutArrayOfNodes.AppendElement(*parent);
    5960           0 :           break;
    5961             :         }
    5962             :       }
    5963             :     }
    5964             :     // If we didn't find any nodes this way, then try the normal way.  Perhaps
    5965             :     // the selection spans multiple lists but with no common list parent.
    5966           0 :     if (!aOutArrayOfNodes.IsEmpty()) {
    5967           0 :       return NS_OK;
    5968             :     }
    5969             :   }
    5970             : 
    5971             :   {
    5972             :     // We don't like other people messing with our selection!
    5973           0 :     AutoTransactionsConserveSelection dontSpazMySelection(htmlEditor);
    5974             : 
    5975             :     // contruct a list of nodes to act on.
    5976           0 :     nsresult rv = GetNodesFromSelection(*selection, EditAction::makeList,
    5977           0 :                                         aOutArrayOfNodes, aTouchContent);
    5978           0 :     NS_ENSURE_SUCCESS(rv, rv);
    5979             :   }
    5980             : 
    5981             :   // Pre-process our list of nodes
    5982           0 :   for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
    5983           0 :     OwningNonNull<nsINode> testNode = aOutArrayOfNodes[i];
    5984             : 
    5985             :     // Remove all non-editable nodes.  Leave them be.
    5986           0 :     if (!htmlEditor->IsEditable(testNode)) {
    5987           0 :       aOutArrayOfNodes.RemoveElementAt(i);
    5988           0 :       continue;
    5989             :     }
    5990             : 
    5991             :     // Scan for table elements and divs.  If we find table elements other than
    5992             :     // table, replace it with a list of any editable non-table content.
    5993           0 :     if (HTMLEditUtils::IsTableElementButNotTable(testNode)) {
    5994           0 :       int32_t j = i;
    5995           0 :       aOutArrayOfNodes.RemoveElementAt(i);
    5996           0 :       GetInnerContent(*testNode, aOutArrayOfNodes, &j, Lists::no);
    5997             :     }
    5998             :   }
    5999             : 
    6000             :   // If there is only one node in the array, and it is a list, div, or
    6001             :   // blockquote, then look inside of it until we find inner list or content.
    6002           0 :   LookInsideDivBQandList(aOutArrayOfNodes);
    6003             : 
    6004           0 :   return NS_OK;
    6005             : }
    6006             : 
    6007             : void
    6008           0 : HTMLEditRules::LookInsideDivBQandList(
    6009             :                  nsTArray<OwningNonNull<nsINode>>& aNodeArray)
    6010             : {
    6011           0 :   NS_ENSURE_TRUE(mHTMLEditor, );
    6012           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    6013             : 
    6014             :   // If there is only one node in the array, and it is a list, div, or
    6015             :   // blockquote, then look inside of it until we find inner list or content.
    6016           0 :   if (aNodeArray.Length() != 1) {
    6017           0 :     return;
    6018             :   }
    6019             : 
    6020           0 :   OwningNonNull<nsINode> curNode = aNodeArray[0];
    6021             : 
    6022           0 :   while (curNode->IsHTMLElement(nsGkAtoms::div) ||
    6023           0 :          HTMLEditUtils::IsList(curNode) ||
    6024           0 :          curNode->IsHTMLElement(nsGkAtoms::blockquote)) {
    6025             :     // Dive as long as there's only one child, and it's a list, div, blockquote
    6026           0 :     uint32_t numChildren = htmlEditor->CountEditableChildren(curNode);
    6027           0 :     if (numChildren != 1) {
    6028           0 :       break;
    6029             :     }
    6030             : 
    6031             :     // Keep diving!  XXX One would expect to dive into the one editable node.
    6032           0 :     nsCOMPtr<nsIContent> child = curNode->GetFirstChild();
    6033           0 :     if (!child->IsHTMLElement(nsGkAtoms::div) &&
    6034           0 :         !HTMLEditUtils::IsList(child) &&
    6035           0 :         !child->IsHTMLElement(nsGkAtoms::blockquote)) {
    6036           0 :       break;
    6037             :     }
    6038             : 
    6039             :     // check editability XXX floppy moose
    6040           0 :     curNode = child;
    6041             :   }
    6042             : 
    6043             :   // We've found innermost list/blockquote/div: replace the one node in the
    6044             :   // array with these nodes
    6045           0 :   aNodeArray.RemoveElementAt(0);
    6046           0 :   if (curNode->IsAnyOfHTMLElements(nsGkAtoms::div,
    6047             :                                    nsGkAtoms::blockquote)) {
    6048           0 :     int32_t j = 0;
    6049           0 :     GetInnerContent(*curNode, aNodeArray, &j, Lists::no, Tables::no);
    6050           0 :     return;
    6051             :   }
    6052             : 
    6053           0 :   aNodeArray.AppendElement(*curNode);
    6054             : }
    6055             : 
    6056             : void
    6057           0 : HTMLEditRules::GetDefinitionListItemTypes(dom::Element* aElement,
    6058             :                                           bool* aDT,
    6059             :                                           bool* aDD)
    6060             : {
    6061           0 :   MOZ_ASSERT(aElement);
    6062           0 :   MOZ_ASSERT(aElement->IsHTMLElement(nsGkAtoms::dl));
    6063           0 :   MOZ_ASSERT(aDT);
    6064           0 :   MOZ_ASSERT(aDD);
    6065             : 
    6066           0 :   *aDT = *aDD = false;
    6067           0 :   for (nsIContent* child = aElement->GetFirstChild();
    6068           0 :        child;
    6069           0 :        child = child->GetNextSibling()) {
    6070           0 :     if (child->IsHTMLElement(nsGkAtoms::dt)) {
    6071           0 :       *aDT = true;
    6072           0 :     } else if (child->IsHTMLElement(nsGkAtoms::dd)) {
    6073           0 :       *aDD = true;
    6074             :     }
    6075             :   }
    6076           0 : }
    6077             : 
    6078             : nsresult
    6079           0 : HTMLEditRules::GetParagraphFormatNodes(
    6080             :                  nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
    6081             :                  TouchContent aTouchContent)
    6082             : {
    6083           0 :   NS_ENSURE_STATE(mHTMLEditor);
    6084           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    6085             : 
    6086           0 :   RefPtr<Selection> selection = htmlEditor->GetSelection();
    6087           0 :   NS_ENSURE_STATE(selection);
    6088             : 
    6089             :   // Contruct a list of nodes to act on.
    6090           0 :   nsresult rv = GetNodesFromSelection(*selection, EditAction::makeBasicBlock,
    6091           0 :                                       outArrayOfNodes, aTouchContent);
    6092           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6093             : 
    6094             :   // Pre-process our list of nodes
    6095           0 :   for (int32_t i = outArrayOfNodes.Length() - 1; i >= 0; i--) {
    6096           0 :     OwningNonNull<nsINode> testNode = outArrayOfNodes[i];
    6097             : 
    6098             :     // Remove all non-editable nodes.  Leave them be.
    6099           0 :     if (!htmlEditor->IsEditable(testNode)) {
    6100           0 :       outArrayOfNodes.RemoveElementAt(i);
    6101           0 :       continue;
    6102             :     }
    6103             : 
    6104             :     // Scan for table elements.  If we find table elements other than table,
    6105             :     // replace it with a list of any editable non-table content.  Ditto for
    6106             :     // list elements.
    6107           0 :     if (HTMLEditUtils::IsTableElement(testNode) ||
    6108           0 :         HTMLEditUtils::IsList(testNode) ||
    6109           0 :         HTMLEditUtils::IsListItem(testNode)) {
    6110           0 :       int32_t j = i;
    6111           0 :       outArrayOfNodes.RemoveElementAt(i);
    6112           0 :       GetInnerContent(testNode, outArrayOfNodes, &j);
    6113             :     }
    6114             :   }
    6115           0 :   return NS_OK;
    6116             : }
    6117             : 
    6118             : nsresult
    6119           0 : HTMLEditRules::BustUpInlinesAtRangeEndpoints(RangeItem& item)
    6120             : {
    6121           0 :   bool isCollapsed = item.mStartContainer == item.mEndContainer &&
    6122           0 :                      item.mStartOffset == item.mEndOffset;
    6123             : 
    6124           0 :   nsCOMPtr<nsIContent> endInline = GetHighestInlineParent(*item.mEndContainer);
    6125             : 
    6126             :   // if we have inline parents above range endpoints, split them
    6127           0 :   if (endInline && !isCollapsed) {
    6128           0 :     nsCOMPtr<nsINode> resultEndNode = endInline->GetParentNode();
    6129           0 :     NS_ENSURE_STATE(mHTMLEditor);
    6130             :     // item.mEndContainer must be content if endInline isn't null
    6131             :     int32_t resultEndOffset =
    6132           0 :       mHTMLEditor->SplitNodeDeep(*endInline, *item.mEndContainer->AsContent(),
    6133             :                                  item.mEndOffset,
    6134           0 :                                  EditorBase::EmptyContainers::no);
    6135           0 :     NS_ENSURE_TRUE(resultEndOffset != -1, NS_ERROR_FAILURE);
    6136             :     // reset range
    6137           0 :     item.mEndContainer = resultEndNode;
    6138           0 :     item.mEndOffset = resultEndOffset;
    6139             :   }
    6140             : 
    6141             :   nsCOMPtr<nsIContent> startInline =
    6142           0 :     GetHighestInlineParent(*item.mStartContainer);
    6143             : 
    6144           0 :   if (startInline) {
    6145           0 :     nsCOMPtr<nsINode> resultStartNode = startInline->GetParentNode();
    6146           0 :     NS_ENSURE_STATE(mHTMLEditor);
    6147             :     int32_t resultStartOffset =
    6148           0 :       mHTMLEditor->SplitNodeDeep(*startInline,
    6149           0 :                                  *item.mStartContainer->AsContent(),
    6150             :                                  item.mStartOffset,
    6151           0 :                                  EditorBase::EmptyContainers::no);
    6152           0 :     NS_ENSURE_TRUE(resultStartOffset != -1, NS_ERROR_FAILURE);
    6153             :     // reset range
    6154           0 :     item.mStartContainer = resultStartNode;
    6155           0 :     item.mStartOffset = resultStartOffset;
    6156             :   }
    6157             : 
    6158           0 :   return NS_OK;
    6159             : }
    6160             : 
    6161             : nsresult
    6162           0 : HTMLEditRules::BustUpInlinesAtBRs(
    6163             :                  nsIContent& aNode,
    6164             :                  nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes)
    6165             : {
    6166           0 :   NS_ENSURE_STATE(mHTMLEditor);
    6167           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    6168             : 
    6169             :   // First build up a list of all the break nodes inside the inline container.
    6170           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfBreaks;
    6171           0 :   BRNodeFunctor functor;
    6172           0 :   DOMIterator iter(aNode);
    6173           0 :   iter.AppendList(functor, arrayOfBreaks);
    6174             : 
    6175             :   // If there aren't any breaks, just put inNode itself in the array
    6176           0 :   if (arrayOfBreaks.IsEmpty()) {
    6177           0 :     aOutArrayOfNodes.AppendElement(aNode);
    6178           0 :     return NS_OK;
    6179             :   }
    6180             : 
    6181             :   // Else we need to bust up inNode along all the breaks
    6182           0 :   nsCOMPtr<nsINode> inlineParentNode = aNode.GetParentNode();
    6183           0 :   nsCOMPtr<nsIContent> splitDeepNode = &aNode;
    6184           0 :   nsCOMPtr<nsIContent> leftNode, rightNode;
    6185             : 
    6186           0 :   for (uint32_t i = 0; i < arrayOfBreaks.Length(); i++) {
    6187           0 :     OwningNonNull<Element> breakNode = *arrayOfBreaks[i]->AsElement();
    6188           0 :     NS_ENSURE_TRUE(splitDeepNode, NS_ERROR_NULL_POINTER);
    6189           0 :     NS_ENSURE_TRUE(breakNode->GetParent(), NS_ERROR_NULL_POINTER);
    6190           0 :     OwningNonNull<nsIContent> splitParentNode = *breakNode->GetParent();
    6191           0 :     int32_t splitOffset = splitParentNode->IndexOf(breakNode);
    6192             : 
    6193             :     int32_t resultOffset =
    6194           0 :       htmlEditor->SplitNodeDeep(*splitDeepNode, splitParentNode, splitOffset,
    6195             :                                 HTMLEditor::EmptyContainers::yes,
    6196           0 :                                 getter_AddRefs(leftNode),
    6197           0 :                                 getter_AddRefs(rightNode));
    6198           0 :     NS_ENSURE_STATE(resultOffset != -1);
    6199             : 
    6200             :     // Put left node in node list
    6201           0 :     if (leftNode) {
    6202             :       // Might not be a left node.  A break might have been at the very
    6203             :       // beginning of inline container, in which case SplitNodeDeep would not
    6204             :       // actually split anything
    6205           0 :       aOutArrayOfNodes.AppendElement(*leftNode);
    6206             :     }
    6207             :     // Move break outside of container and also put in node list
    6208             :     nsresult rv =
    6209           0 :       htmlEditor->MoveNode(breakNode, inlineParentNode, resultOffset);
    6210           0 :     NS_ENSURE_SUCCESS(rv, rv);
    6211           0 :     aOutArrayOfNodes.AppendElement(*breakNode);
    6212             : 
    6213             :     // Now rightNode becomes the new node to split
    6214           0 :     splitDeepNode = rightNode;
    6215             :   }
    6216             :   // Now tack on remaining rightNode, if any, to the list
    6217           0 :   if (rightNode) {
    6218           0 :     aOutArrayOfNodes.AppendElement(*rightNode);
    6219             :   }
    6220           0 :   return NS_OK;
    6221             : }
    6222             : 
    6223             : nsIContent*
    6224           0 : HTMLEditRules::GetHighestInlineParent(nsINode& aNode)
    6225             : {
    6226           0 :   if (!aNode.IsContent() || IsBlockNode(aNode)) {
    6227           0 :     return nullptr;
    6228             :   }
    6229           0 :   OwningNonNull<nsIContent> node = *aNode.AsContent();
    6230             : 
    6231           0 :   while (node->GetParent() && IsInlineNode(*node->GetParent())) {
    6232           0 :     node = *node->GetParent();
    6233             :   }
    6234           0 :   return node;
    6235             : }
    6236             : 
    6237             : /**
    6238             :  * GetNodesFromPoint() constructs a list of nodes from a point that will be
    6239             :  * operated on.
    6240             :  */
    6241             : nsresult
    6242           0 : HTMLEditRules::GetNodesFromPoint(
    6243             :                  EditorDOMPoint aPoint,
    6244             :                  EditAction aOperation,
    6245             :                  nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
    6246             :                  TouchContent aTouchContent)
    6247             : {
    6248           0 :   NS_ENSURE_STATE(aPoint.node);
    6249           0 :   RefPtr<nsRange> range = new nsRange(aPoint.node);
    6250           0 :   nsresult rv = range->SetStart(aPoint.node, aPoint.offset);
    6251           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    6252             : 
    6253             :   // Expand the range to include adjacent inlines
    6254           0 :   PromoteRange(*range, aOperation);
    6255             : 
    6256             :   // Make array of ranges
    6257           0 :   nsTArray<RefPtr<nsRange>> arrayOfRanges;
    6258             : 
    6259             :   // Stuff new opRange into array
    6260           0 :   arrayOfRanges.AppendElement(range);
    6261             : 
    6262             :   // Use these ranges to contruct a list of nodes to act on
    6263             :   rv = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, aOperation,
    6264           0 :                             aTouchContent);
    6265           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6266             : 
    6267           0 :   return NS_OK;
    6268             : }
    6269             : 
    6270             : /**
    6271             :  * GetNodesFromSelection() constructs a list of nodes from the selection that
    6272             :  * will be operated on.
    6273             :  */
    6274             : nsresult
    6275           0 : HTMLEditRules::GetNodesFromSelection(
    6276             :                  Selection& aSelection,
    6277             :                  EditAction aOperation,
    6278             :                  nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
    6279             :                  TouchContent aTouchContent)
    6280             : {
    6281             :   // Promote selection ranges
    6282           0 :   nsTArray<RefPtr<nsRange>> arrayOfRanges;
    6283           0 :   GetPromotedRanges(aSelection, arrayOfRanges, aOperation);
    6284             : 
    6285             :   // Use these ranges to contruct a list of nodes to act on.
    6286             :   nsresult rv = GetNodesForOperation(arrayOfRanges, outArrayOfNodes,
    6287           0 :                                      aOperation, aTouchContent);
    6288           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6289             : 
    6290           0 :   return NS_OK;
    6291             : }
    6292             : 
    6293             : /**
    6294             :  * MakeTransitionList() detects all the transitions in the array, where a
    6295             :  * transition means that adjacent nodes in the array don't have the same parent.
    6296             :  */
    6297             : void
    6298           0 : HTMLEditRules::MakeTransitionList(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
    6299             :                                   nsTArray<bool>& aTransitionArray)
    6300             : {
    6301           0 :   nsCOMPtr<nsINode> prevParent;
    6302             : 
    6303           0 :   aTransitionArray.EnsureLengthAtLeast(aNodeArray.Length());
    6304           0 :   for (uint32_t i = 0; i < aNodeArray.Length(); i++) {
    6305           0 :     if (aNodeArray[i]->GetParentNode() != prevParent) {
    6306             :       // Different parents: transition point
    6307           0 :       aTransitionArray[i] = true;
    6308             :     } else {
    6309             :       // Same parents: these nodes grew up together
    6310           0 :       aTransitionArray[i] = false;
    6311             :     }
    6312           0 :     prevParent = aNodeArray[i]->GetParentNode();
    6313             :   }
    6314           0 : }
    6315             : 
    6316             : /**
    6317             :  * If aNode is the descendant of a listitem, return that li.  But table element
    6318             :  * boundaries are stoppers on the search.  Also stops on the active editor host
    6319             :  * (contenteditable).  Also test if aNode is an li itself.
    6320             :  */
    6321             : Element*
    6322           0 : HTMLEditRules::IsInListItem(nsINode* aNode)
    6323             : {
    6324           0 :   NS_ENSURE_TRUE(aNode, nullptr);
    6325           0 :   if (HTMLEditUtils::IsListItem(aNode)) {
    6326           0 :     return aNode->AsElement();
    6327             :   }
    6328             : 
    6329           0 :   Element* parent = aNode->GetParentElement();
    6330           0 :   while (parent &&
    6331           0 :          mHTMLEditor && mHTMLEditor->IsDescendantOfEditorRoot(parent) &&
    6332           0 :          !HTMLEditUtils::IsTableElement(parent)) {
    6333           0 :     if (HTMLEditUtils::IsListItem(parent)) {
    6334           0 :       return parent;
    6335             :     }
    6336           0 :     parent = parent->GetParentElement();
    6337             :   }
    6338           0 :   return nullptr;
    6339             : }
    6340             : 
    6341             : nsIAtom&
    6342           0 : HTMLEditRules::DefaultParagraphSeparator()
    6343             : {
    6344           0 :   MOZ_ASSERT(mHTMLEditor);
    6345           0 :   if (!mHTMLEditor) {
    6346           0 :     return *nsGkAtoms::div;
    6347             :   }
    6348           0 :   return ParagraphSeparatorElement(mHTMLEditor->GetDefaultParagraphSeparator());
    6349             : }
    6350             : 
    6351             : /**
    6352             :  * ReturnInHeader: do the right thing for returns pressed in headers
    6353             :  */
    6354             : nsresult
    6355           0 : HTMLEditRules::ReturnInHeader(Selection& aSelection,
    6356             :                               Element& aHeader,
    6357             :                               nsINode& aNode,
    6358             :                               int32_t aOffset)
    6359             : {
    6360           0 :   NS_ENSURE_STATE(mHTMLEditor);
    6361           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    6362             : 
    6363             :   // Remember where the header is
    6364           0 :   nsCOMPtr<nsINode> headerParent = aHeader.GetParentNode();
    6365           0 :   int32_t offset = headerParent ? headerParent->IndexOf(&aHeader) : -1;
    6366             : 
    6367             :   // Get ws code to adjust any ws
    6368           0 :   nsCOMPtr<nsINode> node = &aNode;
    6369           0 :   nsresult rv = WSRunObject::PrepareToSplitAcrossBlocks(htmlEditor,
    6370             :                                                         address_of(node),
    6371           0 :                                                         &aOffset);
    6372           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6373             : 
    6374             :   // Split the header
    6375           0 :   NS_ENSURE_STATE(node->IsContent());
    6376           0 :   htmlEditor->SplitNodeDeep(aHeader, *node->AsContent(), aOffset);
    6377             : 
    6378             :   // If the left-hand heading is empty, put a mozbr in it
    6379           0 :   nsCOMPtr<nsIContent> prevItem = htmlEditor->GetPriorHTMLSibling(&aHeader);
    6380           0 :   if (prevItem && HTMLEditUtils::IsHeader(*prevItem)) {
    6381             :     bool isEmptyNode;
    6382           0 :     rv = htmlEditor->IsEmptyNode(prevItem, &isEmptyNode);
    6383           0 :     NS_ENSURE_SUCCESS(rv, rv);
    6384           0 :     if (isEmptyNode) {
    6385           0 :       rv = CreateMozBR(prevItem->AsDOMNode(), 0);
    6386           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6387             :     }
    6388             :   }
    6389             : 
    6390             :   // If the new (righthand) header node is empty, delete it
    6391             :   bool isEmpty;
    6392           0 :   rv = IsEmptyBlock(aHeader, &isEmpty, MozBRCounts::no);
    6393           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6394           0 :   if (isEmpty) {
    6395           0 :     rv = htmlEditor->DeleteNode(&aHeader);
    6396           0 :     NS_ENSURE_SUCCESS(rv, rv);
    6397             :     // Layout tells the caret to blink in a weird place if we don't place a
    6398             :     // break after the header.
    6399             :     nsCOMPtr<nsIContent> sibling =
    6400           0 :       htmlEditor->GetNextHTMLSibling(headerParent, offset + 1);
    6401           0 :     if (!sibling || !sibling->IsHTMLElement(nsGkAtoms::br)) {
    6402           0 :       ClearCachedStyles();
    6403           0 :       htmlEditor->mTypeInState->ClearAllProps();
    6404             : 
    6405             :       // Create a paragraph
    6406           0 :       nsIAtom& paraAtom = DefaultParagraphSeparator();
    6407             :       // We want a wrapper element even if we separate with <br>
    6408             :       nsCOMPtr<Element> pNode =
    6409           0 :         htmlEditor->CreateNode(&paraAtom == nsGkAtoms::br ? nsGkAtoms::p
    6410             :                                                            : &paraAtom,
    6411           0 :                                 headerParent, offset + 1);
    6412           0 :       NS_ENSURE_STATE(pNode);
    6413             : 
    6414             :       // Append a <br> to it
    6415           0 :       nsCOMPtr<Element> brNode = htmlEditor->CreateBR(pNode, 0);
    6416           0 :       NS_ENSURE_STATE(brNode);
    6417             : 
    6418             :       // Set selection to before the break
    6419           0 :       rv = aSelection.Collapse(pNode, 0);
    6420           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6421             :     } else {
    6422           0 :       headerParent = sibling->GetParentNode();
    6423           0 :       offset = headerParent ? headerParent->IndexOf(sibling) : -1;
    6424             :       // Put selection after break
    6425           0 :       rv = aSelection.Collapse(headerParent, offset + 1);
    6426           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6427             :     }
    6428             :   } else {
    6429             :     // Put selection at front of righthand heading
    6430           0 :     rv = aSelection.Collapse(&aHeader, 0);
    6431           0 :     NS_ENSURE_SUCCESS(rv, rv);
    6432             :   }
    6433           0 :   return NS_OK;
    6434             : }
    6435             : 
    6436             : /**
    6437             :  * ReturnInParagraph() does the right thing for returns pressed in paragraphs.
    6438             :  * For our purposes, this means either <p> or <div>, which is not in keeping
    6439             :  * with the semantics of <div>, but is necessary for compatibility with other
    6440             :  * browsers.
    6441             :  */
    6442             : nsresult
    6443           0 : HTMLEditRules::ReturnInParagraph(Selection* aSelection,
    6444             :                                  nsIDOMNode* aPara,
    6445             :                                  nsIDOMNode* aNode,
    6446             :                                  int32_t aOffset,
    6447             :                                  bool* aCancel,
    6448             :                                  bool* aHandled)
    6449             : {
    6450           0 :   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
    6451           0 :   if (!aSelection || !aPara || !node || !aCancel || !aHandled) {
    6452           0 :     return NS_ERROR_NULL_POINTER;
    6453             :   }
    6454           0 :   *aCancel = false;
    6455           0 :   *aHandled = false;
    6456             : 
    6457             :   int32_t offset;
    6458           0 :   nsCOMPtr<nsINode> parent = EditorBase::GetNodeLocation(node, &offset);
    6459             : 
    6460           0 :   NS_ENSURE_STATE(mHTMLEditor);
    6461           0 :   bool doesCRCreateNewP = mHTMLEditor->GetReturnInParagraphCreatesNewParagraph();
    6462             : 
    6463           0 :   bool newBRneeded = false;
    6464           0 :   bool newSelNode = false;
    6465           0 :   nsCOMPtr<nsIContent> sibling;
    6466           0 :   nsCOMPtr<nsIDOMNode> selNode = aNode;
    6467           0 :   int32_t selOffset = aOffset;
    6468             : 
    6469           0 :   NS_ENSURE_STATE(mHTMLEditor);
    6470           0 :   if (aNode == aPara && doesCRCreateNewP) {
    6471             :     // we are at the edges of the block, newBRneeded not needed!
    6472           0 :     sibling = node->AsContent();
    6473           0 :   } else if (EditorBase::IsTextNode(aNode)) {
    6474             :     // at beginning of text node?
    6475           0 :     if (!aOffset) {
    6476             :       // is there a BR prior to it?
    6477           0 :       NS_ENSURE_STATE(mHTMLEditor);
    6478           0 :       sibling = mHTMLEditor->GetPriorHTMLSibling(node);
    6479           0 :       if (!sibling || !mHTMLEditor || !mHTMLEditor->IsVisBreak(sibling) ||
    6480           0 :           TextEditUtils::HasMozAttr(GetAsDOMNode(sibling))) {
    6481           0 :         NS_ENSURE_STATE(mHTMLEditor);
    6482           0 :         newBRneeded = true;
    6483             :       }
    6484           0 :     } else if (aOffset == static_cast<int32_t>(node->Length())) {
    6485             :       // we're at the end of text node...
    6486             :       // is there a BR after to it?
    6487           0 :       NS_ENSURE_STATE(mHTMLEditor);
    6488           0 :       sibling = mHTMLEditor->GetNextHTMLSibling(node);
    6489           0 :       if (!sibling || !mHTMLEditor || !mHTMLEditor->IsVisBreak(sibling) ||
    6490           0 :           TextEditUtils::HasMozAttr(GetAsDOMNode(sibling))) {
    6491           0 :         NS_ENSURE_STATE(mHTMLEditor);
    6492           0 :         newBRneeded = true;
    6493           0 :         offset++;
    6494             :       }
    6495             :     } else {
    6496           0 :       if (doesCRCreateNewP) {
    6497           0 :         nsCOMPtr<nsIDOMNode> tmp;
    6498           0 :         if (NS_WARN_IF(!mHTMLEditor)) {
    6499           0 :           return NS_ERROR_UNEXPECTED;
    6500             :         }
    6501             :         nsresult rv =
    6502           0 :           mHTMLEditor->SplitNode(aNode, aOffset, getter_AddRefs(tmp));
    6503           0 :         NS_ENSURE_SUCCESS(rv, rv);
    6504           0 :         selNode = tmp;
    6505             :       }
    6506             : 
    6507           0 :       newBRneeded = true;
    6508           0 :       offset++;
    6509             :     }
    6510             :   } else {
    6511             :     // not in a text node.
    6512             :     // is there a BR prior to it?
    6513           0 :     nsCOMPtr<nsIContent> nearNode;
    6514           0 :     NS_ENSURE_STATE(mHTMLEditor);
    6515           0 :     nearNode = mHTMLEditor->GetPriorHTMLNode(node, aOffset);
    6516           0 :     NS_ENSURE_STATE(mHTMLEditor);
    6517           0 :     if (!nearNode || !mHTMLEditor->IsVisBreak(nearNode) ||
    6518           0 :         TextEditUtils::HasMozAttr(GetAsDOMNode(nearNode))) {
    6519             :       // is there a BR after it?
    6520           0 :       NS_ENSURE_STATE(mHTMLEditor);
    6521           0 :       nearNode = mHTMLEditor->GetNextHTMLNode(node, aOffset);
    6522           0 :       NS_ENSURE_STATE(mHTMLEditor);
    6523           0 :       if (!nearNode || !mHTMLEditor->IsVisBreak(nearNode) ||
    6524           0 :           TextEditUtils::HasMozAttr(GetAsDOMNode(nearNode))) {
    6525           0 :         newBRneeded = true;
    6526           0 :         parent = node;
    6527           0 :         offset = aOffset;
    6528           0 :         newSelNode = true;
    6529             :       }
    6530             :     }
    6531           0 :     if (!newBRneeded) {
    6532           0 :       sibling = nearNode;
    6533             :     }
    6534             :   }
    6535           0 :   if (newBRneeded) {
    6536             :     // if CR does not create a new P, default to BR creation
    6537           0 :     NS_ENSURE_TRUE(doesCRCreateNewP, NS_OK);
    6538             : 
    6539           0 :     NS_ENSURE_STATE(mHTMLEditor);
    6540           0 :     sibling = mHTMLEditor->CreateBR(parent, offset);
    6541           0 :     if (newSelNode) {
    6542             :       // We split the parent after the br we've just inserted.
    6543           0 :       selNode = GetAsDOMNode(parent);
    6544           0 :       selOffset = offset + 1;
    6545             :     }
    6546             :   }
    6547           0 :   *aHandled = true;
    6548           0 :   return SplitParagraph(aPara, sibling, aSelection, address_of(selNode), &selOffset);
    6549             : }
    6550             : 
    6551             : /**
    6552             :  * SplitParagraph() splits a paragraph at selection point, possibly deleting a
    6553             :  * br.
    6554             :  */
    6555             : nsresult
    6556           0 : HTMLEditRules::SplitParagraph(nsIDOMNode *aPara,
    6557             :                               nsIContent* aBRNode,
    6558             :                               Selection* aSelection,
    6559             :                               nsCOMPtr<nsIDOMNode>* aSelNode,
    6560             :                               int32_t* aOffset)
    6561             : {
    6562           0 :   nsCOMPtr<Element> para = do_QueryInterface(aPara);
    6563           0 :   NS_ENSURE_TRUE(para && aBRNode && aSelNode && *aSelNode && aOffset &&
    6564             :                  aSelection, NS_ERROR_NULL_POINTER);
    6565             : 
    6566             :   // split para
    6567             :   // get ws code to adjust any ws
    6568           0 :   nsCOMPtr<nsIContent> leftPara, rightPara;
    6569           0 :   NS_ENSURE_STATE(mHTMLEditor);
    6570           0 :   nsCOMPtr<nsINode> selNode(do_QueryInterface(*aSelNode));
    6571             :   nsresult rv =
    6572           0 :     WSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor,
    6573           0 :                                             address_of(selNode), aOffset);
    6574             :   // XXX When it fails, why do we need to return selection node?  (Why can the
    6575             :   //     caller trust the result even when it returns error?)
    6576           0 :   *aSelNode = GetAsDOMNode(selNode);
    6577           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6578             :   // split the paragraph
    6579           0 :   NS_ENSURE_STATE(mHTMLEditor);
    6580           0 :   NS_ENSURE_STATE(selNode->IsContent());
    6581             :   int32_t offset =
    6582           0 :     mHTMLEditor->SplitNodeDeep(*para, *selNode->AsContent(), *aOffset,
    6583             :                                HTMLEditor::EmptyContainers::yes,
    6584           0 :                                getter_AddRefs(leftPara),
    6585           0 :                                getter_AddRefs(rightPara));
    6586           0 :   if (NS_WARN_IF(offset == -1)) {
    6587           0 :     return NS_ERROR_FAILURE;
    6588             :   }
    6589             :   // get rid of the break, if it is visible (otherwise it may be needed to prevent an empty p)
    6590           0 :   NS_ENSURE_STATE(mHTMLEditor);
    6591           0 :   if (mHTMLEditor->IsVisBreak(aBRNode)) {
    6592           0 :     NS_ENSURE_STATE(mHTMLEditor);
    6593           0 :     rv = mHTMLEditor->DeleteNode(aBRNode);
    6594           0 :     NS_ENSURE_SUCCESS(rv, rv);
    6595             :   }
    6596             : 
    6597             :   // remove ID attribute on the paragraph we just created
    6598           0 :   RefPtr<Element> rightElt = rightPara->AsElement();
    6599           0 :   NS_ENSURE_STATE(mHTMLEditor);
    6600           0 :   rv = mHTMLEditor->RemoveAttribute(rightElt, nsGkAtoms::id);
    6601           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6602             : 
    6603             :   // check both halves of para to see if we need mozBR
    6604           0 :   rv = InsertMozBRIfNeeded(*leftPara);
    6605           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6606           0 :   rv = InsertMozBRIfNeeded(*rightPara);
    6607           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6608             : 
    6609             :   // selection to beginning of right hand para;
    6610             :   // look inside any containers that are up front.
    6611           0 :   nsCOMPtr<nsINode> rightParaNode = do_QueryInterface(rightPara);
    6612           0 :   NS_ENSURE_STATE(mHTMLEditor && rightParaNode);
    6613             :   nsCOMPtr<nsIDOMNode> child =
    6614           0 :     GetAsDOMNode(mHTMLEditor->GetLeftmostChild(rightParaNode, true));
    6615           0 :   if (EditorBase::IsTextNode(child) ||
    6616           0 :       mHTMLEditor->IsContainer(child)) {
    6617           0 :     aSelection->Collapse(child,0);
    6618             :   } else {
    6619             :     int32_t offset;
    6620           0 :     nsCOMPtr<nsIDOMNode> parent = EditorBase::GetNodeLocation(child, &offset);
    6621           0 :     aSelection->Collapse(parent,offset);
    6622             :   }
    6623           0 :   return NS_OK;
    6624             : }
    6625             : 
    6626             : /**
    6627             :  * ReturnInListItem: do the right thing for returns pressed in list items
    6628             :  */
    6629             : nsresult
    6630           0 : HTMLEditRules::ReturnInListItem(Selection& aSelection,
    6631             :                                 Element& aListItem,
    6632             :                                 nsINode& aNode,
    6633             :                                 int32_t aOffset)
    6634             : {
    6635           0 :   MOZ_ASSERT(HTMLEditUtils::IsListItem(&aListItem));
    6636             : 
    6637           0 :   NS_ENSURE_STATE(mHTMLEditor);
    6638           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    6639             : 
    6640             :   // Get the item parent and the active editing host.
    6641           0 :   nsCOMPtr<Element> root = htmlEditor->GetActiveEditingHost();
    6642             : 
    6643           0 :   nsCOMPtr<Element> list = aListItem.GetParentElement();
    6644           0 :   int32_t itemOffset = list ? list->IndexOf(&aListItem) : -1;
    6645             : 
    6646             :   // If we are in an empty item, then we want to pop up out of the list, but
    6647             :   // only if prefs say it's okay and if the parent isn't the active editing
    6648             :   // host.
    6649             :   bool isEmpty;
    6650           0 :   nsresult rv = IsEmptyBlock(aListItem, &isEmpty, MozBRCounts::no);
    6651           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6652           0 :   if (isEmpty && root != list && mReturnInEmptyLIKillsList) {
    6653             :     // Get the list offset now -- before we might eventually split the list
    6654           0 :     nsCOMPtr<nsINode> listParent = list->GetParentNode();
    6655           0 :     int32_t offset = listParent ? listParent->IndexOf(list) : -1;
    6656             : 
    6657             :     // Are we the last list item in the list?
    6658           0 :     if (!htmlEditor->IsLastEditableChild(&aListItem)) {
    6659             :       // We need to split the list!
    6660           0 :       ErrorResult rv;
    6661           0 :       htmlEditor->SplitNode(*list, itemOffset, rv);
    6662           0 :       NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
    6663             :     }
    6664             : 
    6665             :     // Are we in a sublist?
    6666           0 :     if (HTMLEditUtils::IsList(listParent)) {
    6667             :       // If so, move item out of this list and into the grandparent list
    6668           0 :       rv = htmlEditor->MoveNode(&aListItem, listParent, offset + 1);
    6669           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6670           0 :       rv = aSelection.Collapse(&aListItem, 0);
    6671           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6672             :     } else {
    6673             :       // Otherwise kill this item
    6674           0 :       rv = htmlEditor->DeleteNode(&aListItem);
    6675           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6676             : 
    6677             :       // Time to insert a paragraph
    6678           0 :       nsIAtom& paraAtom = DefaultParagraphSeparator();
    6679             :       // We want a wrapper even if we separate with <br>
    6680             :       nsCOMPtr<Element> pNode =
    6681           0 :         htmlEditor->CreateNode(&paraAtom == nsGkAtoms::br ? nsGkAtoms::p
    6682             :                                                            : &paraAtom,
    6683           0 :                                 listParent, offset + 1);
    6684           0 :       NS_ENSURE_STATE(pNode);
    6685             : 
    6686             :       // Append a <br> to it
    6687           0 :       nsCOMPtr<Element> brNode = htmlEditor->CreateBR(pNode, 0);
    6688           0 :       NS_ENSURE_STATE(brNode);
    6689             : 
    6690             :       // Set selection to before the break
    6691           0 :       rv = aSelection.Collapse(pNode, 0);
    6692           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6693             :     }
    6694           0 :     return NS_OK;
    6695             :   }
    6696             : 
    6697             :   // Else we want a new list item at the same list level.  Get ws code to
    6698             :   // adjust any ws.
    6699           0 :   nsCOMPtr<nsINode> selNode = &aNode;
    6700           0 :   rv = WSRunObject::PrepareToSplitAcrossBlocks(htmlEditor,
    6701           0 :                                                address_of(selNode), &aOffset);
    6702           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6703             :   // Now split list item
    6704           0 :   NS_ENSURE_STATE(selNode->IsContent());
    6705           0 :   htmlEditor->SplitNodeDeep(aListItem, *selNode->AsContent(), aOffset);
    6706             : 
    6707             :   // Hack: until I can change the damaged doc range code back to being
    6708             :   // extra-inclusive, I have to manually detect certain list items that may be
    6709             :   // left empty.
    6710           0 :   nsCOMPtr<nsIContent> prevItem = htmlEditor->GetPriorHTMLSibling(&aListItem);
    6711           0 :   if (prevItem && HTMLEditUtils::IsListItem(prevItem)) {
    6712             :     bool isEmptyNode;
    6713           0 :     rv = htmlEditor->IsEmptyNode(prevItem, &isEmptyNode);
    6714           0 :     NS_ENSURE_SUCCESS(rv, rv);
    6715           0 :     if (isEmptyNode) {
    6716           0 :       rv = CreateMozBR(prevItem->AsDOMNode(), 0);
    6717           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6718             :     } else {
    6719           0 :       rv = htmlEditor->IsEmptyNode(&aListItem, &isEmptyNode, true);
    6720           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6721           0 :       if (isEmptyNode) {
    6722           0 :         nsCOMPtr<nsIAtom> nodeAtom = aListItem.NodeInfo()->NameAtom();
    6723           0 :         if (nodeAtom == nsGkAtoms::dd || nodeAtom == nsGkAtoms::dt) {
    6724           0 :           nsCOMPtr<nsINode> list = aListItem.GetParentNode();
    6725           0 :           int32_t itemOffset = list ? list->IndexOf(&aListItem) : -1;
    6726             : 
    6727           0 :           nsIAtom* listAtom = nodeAtom == nsGkAtoms::dt ? nsGkAtoms::dd
    6728           0 :                                                         : nsGkAtoms::dt;
    6729             :           nsCOMPtr<Element> newListItem =
    6730           0 :             htmlEditor->CreateNode(listAtom, list, itemOffset + 1);
    6731           0 :           NS_ENSURE_STATE(newListItem);
    6732           0 :           rv = htmlEditor->DeleteNode(&aListItem);
    6733           0 :           NS_ENSURE_SUCCESS(rv, rv);
    6734           0 :           rv = aSelection.Collapse(newListItem, 0);
    6735           0 :           NS_ENSURE_SUCCESS(rv, rv);
    6736             : 
    6737           0 :           return NS_OK;
    6738             :         }
    6739             : 
    6740           0 :         nsCOMPtr<Element> brNode;
    6741           0 :         rv = htmlEditor->CopyLastEditableChildStyles(GetAsDOMNode(prevItem),
    6742             :                                                      GetAsDOMNode(&aListItem),
    6743           0 :                                                      getter_AddRefs(brNode));
    6744           0 :         NS_ENSURE_SUCCESS(rv, rv);
    6745           0 :         if (brNode) {
    6746           0 :           nsCOMPtr<nsINode> brParent = brNode->GetParentNode();
    6747           0 :           int32_t offset = brParent ? brParent->IndexOf(brNode) : -1;
    6748           0 :           rv = aSelection.Collapse(brParent, offset);
    6749           0 :           NS_ENSURE_SUCCESS(rv, rv);
    6750           0 :           return NS_OK;
    6751             :         }
    6752             :       } else {
    6753           0 :         WSRunObject wsObj(htmlEditor, &aListItem, 0);
    6754           0 :         nsCOMPtr<nsINode> visNode;
    6755           0 :         int32_t visOffset = 0;
    6756           0 :         WSType wsType;
    6757           0 :         wsObj.NextVisibleNode(&aListItem, 0, address_of(visNode),
    6758           0 :                               &visOffset, &wsType);
    6759           0 :         if (wsType == WSType::special || wsType == WSType::br ||
    6760           0 :             visNode->IsHTMLElement(nsGkAtoms::hr)) {
    6761           0 :           nsCOMPtr<nsINode> parent = visNode->GetParentNode();
    6762           0 :           int32_t offset = parent ? parent->IndexOf(visNode) : -1;
    6763           0 :           rv = aSelection.Collapse(parent, offset);
    6764           0 :           NS_ENSURE_SUCCESS(rv, rv);
    6765           0 :           return NS_OK;
    6766             :         } else {
    6767           0 :           rv = aSelection.Collapse(visNode, visOffset);
    6768           0 :           NS_ENSURE_SUCCESS(rv, rv);
    6769           0 :           return NS_OK;
    6770             :         }
    6771             :       }
    6772             :     }
    6773             :   }
    6774           0 :   rv = aSelection.Collapse(&aListItem, 0);
    6775           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6776           0 :   return NS_OK;
    6777             : }
    6778             : 
    6779             : /**
    6780             :  * MakeBlockquote() puts the list of nodes into one or more blockquotes.
    6781             :  */
    6782             : nsresult
    6783           0 : HTMLEditRules::MakeBlockquote(nsTArray<OwningNonNull<nsINode>>& aNodeArray)
    6784             : {
    6785             :   // The idea here is to put the nodes into a minimal number of blockquotes.
    6786             :   // When the user blockquotes something, they expect one blockquote.  That may
    6787             :   // not be possible (for instance, if they have two table cells selected, you
    6788             :   // need two blockquotes inside the cells).
    6789           0 :   nsCOMPtr<Element> curBlock;
    6790           0 :   nsCOMPtr<nsINode> prevParent;
    6791             : 
    6792           0 :   for (auto& curNode : aNodeArray) {
    6793             :     // Get the node to act on, and its location
    6794           0 :     NS_ENSURE_STATE(curNode->IsContent());
    6795             : 
    6796             :     // If the node is a table element or list item, dive inside
    6797           0 :     if (HTMLEditUtils::IsTableElementButNotTable(curNode) ||
    6798           0 :         HTMLEditUtils::IsListItem(curNode)) {
    6799             :       // Forget any previous block
    6800           0 :       curBlock = nullptr;
    6801             :       // Recursion time
    6802           0 :       nsTArray<OwningNonNull<nsINode>> childArray;
    6803           0 :       GetChildNodesForOperation(*curNode, childArray);
    6804           0 :       nsresult rv = MakeBlockquote(childArray);
    6805           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6806             :     }
    6807             : 
    6808             :     // If the node has different parent than previous node, further nodes in a
    6809             :     // new parent
    6810           0 :     if (prevParent) {
    6811           0 :       if (prevParent != curNode->GetParentNode()) {
    6812             :         // Forget any previous blockquote node we were using
    6813           0 :         curBlock = nullptr;
    6814           0 :         prevParent = curNode->GetParentNode();
    6815             :       }
    6816             :     } else {
    6817           0 :       prevParent = curNode->GetParentNode();
    6818             :     }
    6819             : 
    6820             :     // If no curBlock, make one
    6821           0 :     if (!curBlock) {
    6822           0 :       nsCOMPtr<nsINode> curParent = curNode->GetParentNode();
    6823           0 :       int32_t offset = curParent ? curParent->IndexOf(curNode) : -1;
    6824           0 :       nsresult rv = SplitAsNeeded(*nsGkAtoms::blockquote, curParent, offset);
    6825           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6826           0 :       NS_ENSURE_STATE(mHTMLEditor);
    6827           0 :       curBlock = mHTMLEditor->CreateNode(nsGkAtoms::blockquote, curParent,
    6828           0 :                                          offset);
    6829           0 :       NS_ENSURE_STATE(curBlock);
    6830             :       // remember our new block for postprocessing
    6831           0 :       mNewBlock = curBlock;
    6832             :       // note: doesn't matter if we set mNewBlock multiple times.
    6833             :     }
    6834             : 
    6835           0 :     NS_ENSURE_STATE(mHTMLEditor);
    6836           0 :     nsresult rv = mHTMLEditor->MoveNode(curNode->AsContent(), curBlock, -1);
    6837           0 :     NS_ENSURE_SUCCESS(rv, rv);
    6838             :   }
    6839           0 :   return NS_OK;
    6840             : }
    6841             : 
    6842             : /**
    6843             :  * RemoveBlockStyle() makes the nodes have no special block type.
    6844             :  */
    6845             : nsresult
    6846           0 : HTMLEditRules::RemoveBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray)
    6847             : {
    6848           0 :   NS_ENSURE_STATE(mHTMLEditor);
    6849           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    6850             : 
    6851             :   // Intent of this routine is to be used for converting to/from headers,
    6852             :   // paragraphs, pre, and address.  Those blocks that pretty much just contain
    6853             :   // inline things...
    6854           0 :   nsCOMPtr<Element> curBlock;
    6855           0 :   nsCOMPtr<nsIContent> firstNode, lastNode;
    6856           0 :   for (auto& curNode : aNodeArray) {
    6857             :     // If curNode is a address, p, header, address, or pre, remove it
    6858           0 :     if (HTMLEditUtils::IsFormatNode(curNode)) {
    6859             :       // Process any partial progress saved
    6860           0 :       if (curBlock) {
    6861           0 :         nsresult rv = RemovePartOfBlock(*curBlock, *firstNode, *lastNode);
    6862           0 :         NS_ENSURE_SUCCESS(rv, rv);
    6863           0 :         firstNode = lastNode = curBlock = nullptr;
    6864             :       }
    6865           0 :       if (!mHTMLEditor->IsEditable(curNode)) {
    6866           0 :         continue;
    6867             :       }
    6868             :       // Remove current block
    6869           0 :       nsresult rv = htmlEditor->RemoveBlockContainer(*curNode->AsContent());
    6870           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6871           0 :     } else if (curNode->IsAnyOfHTMLElements(nsGkAtoms::table,
    6872             :                                             nsGkAtoms::tr,
    6873             :                                             nsGkAtoms::tbody,
    6874             :                                             nsGkAtoms::td,
    6875             :                                             nsGkAtoms::li,
    6876             :                                             nsGkAtoms::blockquote,
    6877           0 :                                             nsGkAtoms::div) ||
    6878           0 :                 HTMLEditUtils::IsList(curNode)) {
    6879             :       // Process any partial progress saved
    6880           0 :       if (curBlock) {
    6881           0 :         nsresult rv = RemovePartOfBlock(*curBlock, *firstNode, *lastNode);
    6882           0 :         NS_ENSURE_SUCCESS(rv, rv);
    6883           0 :         firstNode = lastNode = curBlock = nullptr;
    6884             :       }
    6885           0 :       if (!mHTMLEditor->IsEditable(curNode)) {
    6886           0 :         continue;
    6887             :       }
    6888             :       // Recursion time
    6889           0 :       nsTArray<OwningNonNull<nsINode>> childArray;
    6890           0 :       GetChildNodesForOperation(*curNode, childArray);
    6891           0 :       nsresult rv = RemoveBlockStyle(childArray);
    6892           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6893           0 :     } else if (IsInlineNode(curNode)) {
    6894           0 :       if (curBlock) {
    6895             :         // If so, is this node a descendant?
    6896           0 :         if (EditorUtils::IsDescendantOf(curNode, curBlock)) {
    6897             :           // Then we don't need to do anything different for this node
    6898           0 :           lastNode = curNode->AsContent();
    6899           0 :           continue;
    6900             :         }
    6901             :         // Otherwise, we have progressed beyond end of curBlock, so let's
    6902             :         // handle it now.  We need to remove the portion of curBlock that
    6903             :         // contains [firstNode - lastNode].
    6904           0 :         nsresult rv = RemovePartOfBlock(*curBlock, *firstNode, *lastNode);
    6905           0 :         NS_ENSURE_SUCCESS(rv, rv);
    6906           0 :         firstNode = lastNode = curBlock = nullptr;
    6907             :         // Fall out and handle curNode
    6908             :       }
    6909           0 :       curBlock = htmlEditor->GetBlockNodeParent(curNode);
    6910           0 :       if (!curBlock || !HTMLEditUtils::IsFormatNode(curBlock) ||
    6911           0 :           !mHTMLEditor->IsEditable(curBlock)) {
    6912             :         // Not a block kind that we care about.
    6913           0 :         curBlock = nullptr;
    6914             :       } else {
    6915           0 :         firstNode = lastNode = curNode->AsContent();
    6916             :       }
    6917           0 :     } else if (curBlock) {
    6918             :       // Some node that is already sans block style.  Skip over it and process
    6919             :       // any partial progress saved.
    6920           0 :       nsresult rv = RemovePartOfBlock(*curBlock, *firstNode, *lastNode);
    6921           0 :       NS_ENSURE_SUCCESS(rv, rv);
    6922           0 :       firstNode = lastNode = curBlock = nullptr;
    6923             :     }
    6924             :   }
    6925             :   // Process any partial progress saved
    6926           0 :   if (curBlock) {
    6927           0 :     nsresult rv = RemovePartOfBlock(*curBlock, *firstNode, *lastNode);
    6928           0 :     NS_ENSURE_SUCCESS(rv, rv);
    6929           0 :     firstNode = lastNode = curBlock = nullptr;
    6930             :   }
    6931           0 :   return NS_OK;
    6932             : }
    6933             : 
    6934             : /**
    6935             :  * ApplyBlockStyle() does whatever it takes to make the list of nodes into one
    6936             :  * or more blocks of type aBlockTag.
    6937             :  */
    6938             : nsresult
    6939           0 : HTMLEditRules::ApplyBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
    6940             :                                nsIAtom& aBlockTag)
    6941             : {
    6942             :   // Intent of this routine is to be used for converting to/from headers,
    6943             :   // paragraphs, pre, and address.  Those blocks that pretty much just contain
    6944             :   // inline things...
    6945           0 :   NS_ENSURE_STATE(mHTMLEditor);
    6946           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    6947             : 
    6948           0 :   nsCOMPtr<Element> newBlock;
    6949             : 
    6950           0 :   nsCOMPtr<Element> curBlock;
    6951           0 :   for (auto& curNode : aNodeArray) {
    6952           0 :     nsCOMPtr<nsINode> curParent = curNode->GetParentNode();
    6953           0 :     int32_t offset = curParent ? curParent->IndexOf(curNode) : -1;
    6954             : 
    6955             :     // Is it already the right kind of block, or an uneditable block?
    6956           0 :     if (curNode->IsHTMLElement(&aBlockTag) ||
    6957           0 :         (!mHTMLEditor->IsEditable(curNode) && IsBlockNode(curNode))) {
    6958             :       // Forget any previous block used for previous inline nodes
    6959           0 :       curBlock = nullptr;
    6960             :       // Do nothing to this block
    6961           0 :       continue;
    6962             :     }
    6963             : 
    6964             :     // If curNode is a address, p, header, address, or pre, replace it with a
    6965             :     // new block of correct type.
    6966             :     // XXX: pre can't hold everything the others can
    6967           0 :     if (HTMLEditUtils::IsMozDiv(curNode) ||
    6968           0 :         HTMLEditUtils::IsFormatNode(curNode)) {
    6969             :       // Forget any previous block used for previous inline nodes
    6970           0 :       curBlock = nullptr;
    6971           0 :       newBlock = htmlEditor->ReplaceContainer(curNode->AsElement(),
    6972             :                                               &aBlockTag, nullptr, nullptr,
    6973           0 :                                               EditorBase::eCloneAttributes);
    6974           0 :       NS_ENSURE_STATE(newBlock);
    6975           0 :     } else if (HTMLEditUtils::IsTable(curNode) ||
    6976           0 :                HTMLEditUtils::IsList(curNode) ||
    6977           0 :                curNode->IsAnyOfHTMLElements(nsGkAtoms::tbody,
    6978             :                                             nsGkAtoms::tr,
    6979             :                                             nsGkAtoms::td,
    6980             :                                             nsGkAtoms::li,
    6981             :                                             nsGkAtoms::blockquote,
    6982             :                                             nsGkAtoms::div)) {
    6983             :       // Forget any previous block used for previous inline nodes
    6984           0 :       curBlock = nullptr;
    6985             :       // Recursion time
    6986           0 :       nsTArray<OwningNonNull<nsINode>> childArray;
    6987           0 :       GetChildNodesForOperation(*curNode, childArray);
    6988           0 :       if (!childArray.IsEmpty()) {
    6989           0 :         nsresult rv = ApplyBlockStyle(childArray, aBlockTag);
    6990           0 :         NS_ENSURE_SUCCESS(rv, rv);
    6991             :       } else {
    6992             :         // Make sure we can put a block here
    6993           0 :         nsresult rv = SplitAsNeeded(aBlockTag, curParent, offset);
    6994           0 :         NS_ENSURE_SUCCESS(rv, rv);
    6995             :         nsCOMPtr<Element> theBlock =
    6996           0 :           htmlEditor->CreateNode(&aBlockTag, curParent, offset);
    6997           0 :         NS_ENSURE_STATE(theBlock);
    6998             :         // Remember our new block for postprocessing
    6999           0 :         mNewBlock = theBlock;
    7000             :       }
    7001           0 :     } else if (curNode->IsHTMLElement(nsGkAtoms::br)) {
    7002             :       // If the node is a break, we honor it by putting further nodes in a new
    7003             :       // parent
    7004           0 :       if (curBlock) {
    7005             :         // Forget any previous block used for previous inline nodes
    7006           0 :         curBlock = nullptr;
    7007           0 :         nsresult rv = htmlEditor->DeleteNode(curNode);
    7008           0 :         NS_ENSURE_SUCCESS(rv, rv);
    7009             :       } else {
    7010             :         // The break is the first (or even only) node we encountered.  Create a
    7011             :         // block for it.
    7012           0 :         nsresult rv = SplitAsNeeded(aBlockTag, curParent, offset);
    7013           0 :         NS_ENSURE_SUCCESS(rv, rv);
    7014           0 :         curBlock = htmlEditor->CreateNode(&aBlockTag, curParent, offset);
    7015           0 :         NS_ENSURE_STATE(curBlock);
    7016             :         // Remember our new block for postprocessing
    7017           0 :         mNewBlock = curBlock;
    7018             :         // Note: doesn't matter if we set mNewBlock multiple times.
    7019           0 :         rv = htmlEditor->MoveNode(curNode->AsContent(), curBlock, -1);
    7020           0 :         NS_ENSURE_SUCCESS(rv, rv);
    7021             :       }
    7022           0 :     } else if (IsInlineNode(curNode)) {
    7023             :       // If curNode is inline, pull it into curBlock.  Note: it's assumed that
    7024             :       // consecutive inline nodes in aNodeArray are actually members of the
    7025             :       // same block parent.  This happens to be true now as a side effect of
    7026             :       // how aNodeArray is contructed, but some additional logic should be
    7027             :       // added here if that should change
    7028             :       //
    7029             :       // If curNode is a non editable, drop it if we are going to <pre>.
    7030           0 :       if (&aBlockTag == nsGkAtoms::pre && !htmlEditor->IsEditable(curNode)) {
    7031             :         // Do nothing to this block
    7032           0 :         continue;
    7033             :       }
    7034             : 
    7035             :       // If no curBlock, make one
    7036           0 :       if (!curBlock) {
    7037           0 :         nsresult rv = SplitAsNeeded(aBlockTag, curParent, offset);
    7038           0 :         NS_ENSURE_SUCCESS(rv, rv);
    7039           0 :         curBlock = htmlEditor->CreateNode(&aBlockTag, curParent, offset);
    7040           0 :         NS_ENSURE_STATE(curBlock);
    7041             :         // Remember our new block for postprocessing
    7042           0 :         mNewBlock = curBlock;
    7043             :         // Note: doesn't matter if we set mNewBlock multiple times.
    7044             :       }
    7045             : 
    7046           0 :       if (NS_WARN_IF(!curNode->GetParentNode())) {
    7047             :         // This is possible due to mutation events, let's not assert
    7048           0 :         return NS_ERROR_UNEXPECTED;
    7049             :       }
    7050             : 
    7051             :       // XXX If curNode is a br, replace it with a return if going to <pre>
    7052             : 
    7053             :       // This is a continuation of some inline nodes that belong together in
    7054             :       // the same block item.  Use curBlock.
    7055           0 :       nsresult rv = htmlEditor->MoveNode(curNode->AsContent(), curBlock, -1);
    7056           0 :       NS_ENSURE_SUCCESS(rv, rv);
    7057             :     }
    7058             :   }
    7059           0 :   return NS_OK;
    7060             : }
    7061             : 
    7062             : /**
    7063             :  * Given a tag name, split inOutParent up to the point where we can insert the
    7064             :  * tag.  Adjust inOutParent and inOutOffset to point to new location for tag.
    7065             :  */
    7066             : nsresult
    7067           0 : HTMLEditRules::SplitAsNeeded(nsIAtom& aTag,
    7068             :                              OwningNonNull<nsINode>& aInOutParent,
    7069             :                              int32_t& aInOutOffset)
    7070             : {
    7071             :   // XXX Is there a better way to do this?
    7072           0 :   nsCOMPtr<nsINode> parent = aInOutParent.forget();
    7073           0 :   nsresult rv = SplitAsNeeded(aTag, parent, aInOutOffset);
    7074           0 :   aInOutParent = parent.forget();
    7075           0 :   return rv;
    7076             : }
    7077             : 
    7078             : nsresult
    7079           0 : HTMLEditRules::SplitAsNeeded(nsIAtom& aTag,
    7080             :                              nsCOMPtr<nsINode>& inOutParent,
    7081             :                              int32_t& inOutOffset)
    7082             : {
    7083           0 :   NS_ENSURE_TRUE(inOutParent, NS_ERROR_NULL_POINTER);
    7084             : 
    7085             :   // Check that we have a place that can legally contain the tag
    7086           0 :   nsCOMPtr<nsINode> tagParent, splitNode;
    7087           0 :   for (nsCOMPtr<nsINode> parent = inOutParent; parent;
    7088           0 :        parent = parent->GetParentNode()) {
    7089             :     // Sniffing up the parent tree until we find a legal place for the block
    7090             : 
    7091             :     // Don't leave the active editing host
    7092           0 :     NS_ENSURE_STATE(mHTMLEditor);
    7093           0 :     if (!mHTMLEditor->IsDescendantOfEditorRoot(parent)) {
    7094             :       // XXX Why do we need to check mHTMLEditor again here?
    7095           0 :       NS_ENSURE_STATE(mHTMLEditor);
    7096           0 :       if (parent != mHTMLEditor->GetActiveEditingHost()) {
    7097           0 :         return NS_ERROR_FAILURE;
    7098             :       }
    7099             :     }
    7100             : 
    7101           0 :     NS_ENSURE_STATE(mHTMLEditor);
    7102           0 :     if (mHTMLEditor->CanContainTag(*parent, aTag)) {
    7103             :       // Success
    7104           0 :       tagParent = parent;
    7105           0 :       break;
    7106             :     }
    7107             : 
    7108           0 :     splitNode = parent;
    7109             :   }
    7110           0 :   if (!tagParent) {
    7111             :     // Could not find a place to build tag!
    7112           0 :     return NS_ERROR_FAILURE;
    7113             :   }
    7114           0 :   if (splitNode && splitNode->IsContent() && inOutParent->IsContent()) {
    7115             :     // We found a place for block, but above inOutParent. We need to split.
    7116           0 :     NS_ENSURE_STATE(mHTMLEditor);
    7117           0 :     int32_t offset = mHTMLEditor->SplitNodeDeep(*splitNode->AsContent(),
    7118           0 :                                                 *inOutParent->AsContent(),
    7119           0 :                                                 inOutOffset);
    7120           0 :     NS_ENSURE_STATE(offset != -1);
    7121           0 :     inOutParent = tagParent;
    7122           0 :     inOutOffset = offset;
    7123             :   }
    7124           0 :   return NS_OK;
    7125             : }
    7126             : 
    7127             : /**
    7128             :  * JoinNodesSmart: Join two nodes, doing whatever makes sense for their
    7129             :  * children (which often means joining them, too).  aNodeLeft & aNodeRight must
    7130             :  * be same type of node.
    7131             :  *
    7132             :  * Returns the point where they're merged, or (nullptr, -1) on failure.
    7133             :  */
    7134             : EditorDOMPoint
    7135           0 : HTMLEditRules::JoinNodesSmart(nsIContent& aNodeLeft,
    7136             :                               nsIContent& aNodeRight)
    7137             : {
    7138             :   // Caller responsible for left and right node being the same type
    7139           0 :   nsCOMPtr<nsINode> parent = aNodeLeft.GetParentNode();
    7140           0 :   NS_ENSURE_TRUE(parent, EditorDOMPoint());
    7141           0 :   int32_t parOffset = parent->IndexOf(&aNodeLeft);
    7142           0 :   nsCOMPtr<nsINode> rightParent = aNodeRight.GetParentNode();
    7143             : 
    7144             :   // If they don't have the same parent, first move the right node to after the
    7145             :   // left one
    7146           0 :   if (parent != rightParent) {
    7147           0 :     NS_ENSURE_TRUE(mHTMLEditor, EditorDOMPoint());
    7148           0 :     nsresult rv = mHTMLEditor->MoveNode(&aNodeRight, parent, parOffset);
    7149           0 :     NS_ENSURE_SUCCESS(rv, EditorDOMPoint());
    7150             :   }
    7151             : 
    7152           0 :   EditorDOMPoint ret(&aNodeRight, aNodeLeft.Length());
    7153             : 
    7154             :   // Separate join rules for differing blocks
    7155           0 :   if (HTMLEditUtils::IsList(&aNodeLeft) || aNodeLeft.GetAsText()) {
    7156             :     // For lists, merge shallow (wouldn't want to combine list items)
    7157           0 :     nsresult rv = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight);
    7158           0 :     NS_ENSURE_SUCCESS(rv, EditorDOMPoint());
    7159           0 :     return ret;
    7160             :   }
    7161             : 
    7162             :   // Remember the last left child, and first right child
    7163           0 :   NS_ENSURE_TRUE(mHTMLEditor, EditorDOMPoint());
    7164           0 :   nsCOMPtr<nsIContent> lastLeft = mHTMLEditor->GetLastEditableChild(aNodeLeft);
    7165           0 :   NS_ENSURE_TRUE(lastLeft, EditorDOMPoint());
    7166             : 
    7167           0 :   NS_ENSURE_TRUE(mHTMLEditor, EditorDOMPoint());
    7168           0 :   nsCOMPtr<nsIContent> firstRight = mHTMLEditor->GetFirstEditableChild(aNodeRight);
    7169           0 :   NS_ENSURE_TRUE(firstRight, EditorDOMPoint());
    7170             : 
    7171             :   // For list items, divs, etc., merge smart
    7172           0 :   NS_ENSURE_TRUE(mHTMLEditor, EditorDOMPoint());
    7173           0 :   nsresult rv = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight);
    7174           0 :   NS_ENSURE_SUCCESS(rv, EditorDOMPoint());
    7175             : 
    7176           0 :   if (lastLeft && firstRight && mHTMLEditor &&
    7177           0 :       mHTMLEditor->AreNodesSameType(lastLeft, firstRight) &&
    7178           0 :       (lastLeft->GetAsText() || !mHTMLEditor ||
    7179           0 :        (lastLeft->IsElement() && firstRight->IsElement() &&
    7180           0 :         mHTMLEditor->mCSSEditUtils->ElementsSameStyle(lastLeft->AsElement(),
    7181           0 :                                                   firstRight->AsElement())))) {
    7182           0 :     NS_ENSURE_TRUE(mHTMLEditor, EditorDOMPoint());
    7183           0 :     return JoinNodesSmart(*lastLeft, *firstRight);
    7184             :   }
    7185           0 :   return ret;
    7186             : }
    7187             : 
    7188             : Element*
    7189           0 : HTMLEditRules::GetTopEnclosingMailCite(nsINode& aNode)
    7190             : {
    7191           0 :   nsCOMPtr<Element> ret;
    7192             : 
    7193           0 :   for (nsCOMPtr<nsINode> node = &aNode; node; node = node->GetParentNode()) {
    7194           0 :     if ((IsPlaintextEditor() && node->IsHTMLElement(nsGkAtoms::pre)) ||
    7195           0 :         HTMLEditUtils::IsMailCite(node)) {
    7196           0 :       ret = node->AsElement();
    7197             :     }
    7198           0 :     if (node->IsHTMLElement(nsGkAtoms::body)) {
    7199           0 :       break;
    7200             :     }
    7201             :   }
    7202             : 
    7203           0 :   return ret;
    7204             : }
    7205             : 
    7206             : nsresult
    7207           0 : HTMLEditRules::CacheInlineStyles(nsIDOMNode* aNode)
    7208             : {
    7209           0 :   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
    7210             : 
    7211           0 :   NS_ENSURE_STATE(mHTMLEditor);
    7212             : 
    7213           0 :   nsresult rv = GetInlineStyles(aNode, mCachedStyles);
    7214           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    7215           0 :     return rv;
    7216             :   }
    7217           0 :   return NS_OK;
    7218             : }
    7219             : 
    7220             : nsresult
    7221           0 : HTMLEditRules::GetInlineStyles(nsIDOMNode* aNode,
    7222             :                                StyleCache aStyleCache[SIZE_STYLE_TABLE])
    7223             : {
    7224           0 :   MOZ_ASSERT(aNode);
    7225           0 :   MOZ_ASSERT(mHTMLEditor);
    7226             : 
    7227           0 :   bool useCSS = mHTMLEditor->IsCSSEnabled();
    7228             : 
    7229           0 :   for (size_t j = 0; j < SIZE_STYLE_TABLE; ++j) {
    7230             :     // If type-in state is set, don't intervene
    7231             :     bool typeInSet, unused;
    7232           0 :     if (NS_WARN_IF(!mHTMLEditor)) {
    7233           0 :       return NS_ERROR_UNEXPECTED;
    7234             :     }
    7235           0 :     mHTMLEditor->mTypeInState->GetTypingState(typeInSet, unused,
    7236           0 :       aStyleCache[j].tag, aStyleCache[j].attr, nullptr);
    7237           0 :     if (typeInSet) {
    7238           0 :       continue;
    7239             :     }
    7240             : 
    7241           0 :     bool isSet = false;
    7242           0 :     nsAutoString outValue;
    7243             :     // Don't use CSS for <font size>, we don't support it usefully (bug 780035)
    7244           0 :     if (!useCSS || (aStyleCache[j].tag == nsGkAtoms::font &&
    7245           0 :                     aStyleCache[j].attr.EqualsLiteral("size"))) {
    7246           0 :       NS_ENSURE_STATE(mHTMLEditor);
    7247           0 :       mHTMLEditor->IsTextPropertySetByContent(aNode, aStyleCache[j].tag,
    7248           0 :                                               &(aStyleCache[j].attr), nullptr,
    7249           0 :                                               isSet, &outValue);
    7250             :     } else {
    7251           0 :       NS_ENSURE_STATE(mHTMLEditor);
    7252           0 :       isSet = mHTMLEditor->mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(
    7253           0 :                 aNode, aStyleCache[j].tag, &(aStyleCache[j].attr), outValue,
    7254             :                 CSSEditUtils::eComputed);
    7255             :     }
    7256           0 :     if (isSet) {
    7257           0 :       aStyleCache[j].mPresent = true;
    7258           0 :       aStyleCache[j].value.Assign(outValue);
    7259             :     }
    7260             :   }
    7261           0 :   return NS_OK;
    7262             : }
    7263             : 
    7264             : nsresult
    7265           0 : HTMLEditRules::ReapplyCachedStyles()
    7266             : {
    7267             :   // The idea here is to examine our cached list of styles and see if any have
    7268             :   // been removed.  If so, add typeinstate for them, so that they will be
    7269             :   // reinserted when new content is added.
    7270             : 
    7271             :   // remember if we are in css mode
    7272           0 :   NS_ENSURE_STATE(mHTMLEditor);
    7273           0 :   bool useCSS = mHTMLEditor->IsCSSEnabled();
    7274             : 
    7275             :   // get selection point; if it doesn't exist, we have nothing to do
    7276           0 :   NS_ENSURE_STATE(mHTMLEditor);
    7277           0 :   RefPtr<Selection> selection = mHTMLEditor->GetSelection();
    7278           0 :   if (!selection) {
    7279             :     // If the document is removed from its parent document during executing an
    7280             :     // editor operation with DOMMutationEvent or something, there may be no
    7281             :     // selection.
    7282           0 :     return NS_OK;
    7283             :   }
    7284           0 :   if (!selection->RangeCount()) {
    7285             :     // Nothing to do
    7286           0 :     return NS_OK;
    7287             :   }
    7288             :   nsCOMPtr<nsIContent> selNode =
    7289           0 :     do_QueryInterface(selection->GetRangeAt(0)->GetStartContainer());
    7290           0 :   if (!selNode) {
    7291             :     // Nothing to do
    7292           0 :     return NS_OK;
    7293             :   }
    7294             : 
    7295           0 :   StyleCache styleAtInsertionPoint[SIZE_STYLE_TABLE];
    7296           0 :   InitStyleCacheArray(styleAtInsertionPoint);
    7297           0 :   nsCOMPtr<nsIDOMNode> selDOMNode = do_QueryInterface(selNode);
    7298           0 :   MOZ_ASSERT(selDOMNode);
    7299           0 :   nsresult rv = GetInlineStyles(selDOMNode, styleAtInsertionPoint);
    7300           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    7301           0 :     return NS_OK;
    7302             :   }
    7303             : 
    7304           0 :   for (size_t i = 0; i < SIZE_STYLE_TABLE; ++i) {
    7305           0 :     if (mCachedStyles[i].mPresent) {
    7306             :       bool bFirst, bAny, bAll;
    7307           0 :       bFirst = bAny = bAll = false;
    7308             : 
    7309           0 :       nsAutoString curValue;
    7310           0 :       if (useCSS) {
    7311             :         // check computed style first in css case
    7312           0 :         NS_ENSURE_STATE(mHTMLEditor);
    7313           0 :         bAny = mHTMLEditor->mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(
    7314             :           selNode, mCachedStyles[i].tag, &(mCachedStyles[i].attr), curValue,
    7315           0 :           CSSEditUtils::eComputed);
    7316             :       }
    7317           0 :       if (!bAny) {
    7318             :         // then check typeinstate and html style
    7319           0 :         NS_ENSURE_STATE(mHTMLEditor);
    7320             :         nsresult rv =
    7321           0 :           mHTMLEditor->GetInlinePropertyBase(*mCachedStyles[i].tag,
    7322             :                                              &(mCachedStyles[i].attr),
    7323             :                                              &(mCachedStyles[i].value),
    7324             :                                              &bFirst, &bAny, &bAll,
    7325           0 :                                              &curValue, false);
    7326           0 :         NS_ENSURE_SUCCESS(rv, rv);
    7327             :       }
    7328             :       // This style has disappeared through deletion.  Let's add the styles to
    7329             :       // mTypeInState when same style isn't applied to the node already.
    7330           0 :       if ((!bAny || IsStyleCachePreservingAction(mTheAction)) &&
    7331           0 :            (!styleAtInsertionPoint[i].mPresent ||
    7332           0 :             styleAtInsertionPoint[i].value != mCachedStyles[i].value)) {
    7333           0 :         NS_ENSURE_STATE(mHTMLEditor);
    7334           0 :         mHTMLEditor->mTypeInState->SetProp(mCachedStyles[i].tag,
    7335             :                                            mCachedStyles[i].attr,
    7336           0 :                                            mCachedStyles[i].value);
    7337             :       }
    7338             :     }
    7339             :   }
    7340             : 
    7341           0 :   return NS_OK;
    7342             : }
    7343             : 
    7344             : void
    7345           0 : HTMLEditRules::ClearCachedStyles()
    7346             : {
    7347             :   // clear the mPresent bits in mCachedStyles array
    7348           0 :   for (size_t j = 0; j < SIZE_STYLE_TABLE; j++) {
    7349           0 :     mCachedStyles[j].mPresent = false;
    7350           0 :     mCachedStyles[j].value.Truncate();
    7351             :   }
    7352           0 : }
    7353             : 
    7354             : void
    7355           0 : HTMLEditRules::AdjustSpecialBreaks()
    7356             : {
    7357           0 :   NS_ENSURE_TRUE_VOID(mHTMLEditor);
    7358             : 
    7359             :   // Gather list of empty nodes
    7360           0 :   nsTArray<OwningNonNull<nsINode>> nodeArray;
    7361           0 :   EmptyEditableFunctor functor(mHTMLEditor);
    7362           0 :   DOMIterator iter;
    7363           0 :   if (NS_WARN_IF(NS_FAILED(iter.Init(*mDocChangeRange)))) {
    7364           0 :     return;
    7365             :   }
    7366           0 :   iter.AppendList(functor, nodeArray);
    7367             : 
    7368             :   // Put moz-br's into these empty li's and td's
    7369           0 :   for (auto& node : nodeArray) {
    7370             :     // Need to put br at END of node.  It may have empty containers in it and
    7371             :     // still pass the "IsEmptyNode" test, and we want the br's to be after
    7372             :     // them.  Also, we want the br to be after the selection if the selection
    7373             :     // is in this node.
    7374           0 :     nsresult rv = CreateMozBR(node->AsDOMNode(), (int32_t)node->Length());
    7375           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    7376           0 :       return;
    7377             :     }
    7378             :   }
    7379             : }
    7380             : 
    7381             : nsresult
    7382           0 : HTMLEditRules::AdjustWhitespace(Selection* aSelection)
    7383             : {
    7384             :   // get selection point
    7385           0 :   nsCOMPtr<nsIDOMNode> selNode;
    7386             :   int32_t selOffset;
    7387             :   nsresult rv =
    7388           0 :     EditorBase::GetStartNodeAndOffset(aSelection,
    7389           0 :                                       getter_AddRefs(selNode), &selOffset);
    7390           0 :   NS_ENSURE_SUCCESS(rv, rv);
    7391             : 
    7392             :   // ask whitespace object to tweak nbsp's
    7393           0 :   NS_ENSURE_STATE(mHTMLEditor);
    7394           0 :   return WSRunObject(mHTMLEditor, selNode, selOffset).AdjustWhitespace();
    7395             : }
    7396             : 
    7397             : nsresult
    7398           0 : HTMLEditRules::PinSelectionToNewBlock(Selection* aSelection)
    7399             : {
    7400           0 :   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
    7401           0 :   if (!aSelection->Collapsed()) {
    7402           0 :     return NS_OK;
    7403             :   }
    7404             : 
    7405           0 :   if (NS_WARN_IF(!mNewBlock)) {
    7406           0 :     return NS_ERROR_NULL_POINTER;
    7407             :   }
    7408             : 
    7409             :   // get the (collapsed) selection location
    7410           0 :   nsCOMPtr<nsIDOMNode> selNode;
    7411             :   int32_t selOffset;
    7412             :   nsresult rv =
    7413           0 :     EditorBase::GetStartNodeAndOffset(aSelection,
    7414           0 :                                       getter_AddRefs(selNode), &selOffset);
    7415           0 :   NS_ENSURE_SUCCESS(rv, rv);
    7416             : 
    7417             :   // use ranges and sRangeHelper to compare sel point to new block
    7418           0 :   nsCOMPtr<nsINode> node = do_QueryInterface(selNode);
    7419           0 :   NS_ENSURE_STATE(node);
    7420           0 :   RefPtr<nsRange> range = new nsRange(node);
    7421           0 :   rv = range->CollapseTo(node, selOffset);
    7422           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    7423           0 :     return rv;
    7424             :   }
    7425             :   bool nodeBefore, nodeAfter;
    7426           0 :   rv = nsRange::CompareNodeToRange(mNewBlock, range, &nodeBefore, &nodeAfter);
    7427           0 :   NS_ENSURE_SUCCESS(rv, rv);
    7428             : 
    7429           0 :   if (nodeBefore && nodeAfter) {
    7430           0 :     return NS_OK;  // selection is inside block
    7431           0 :   } else if (nodeBefore) {
    7432             :     // selection is after block.  put at end of block.
    7433           0 :     NS_ENSURE_STATE(mHTMLEditor);
    7434           0 :     nsCOMPtr<nsINode> tmp = mHTMLEditor->GetLastEditableChild(*mNewBlock);
    7435           0 :     if (!tmp) {
    7436           0 :       tmp = mNewBlock;
    7437             :     }
    7438             :     uint32_t endPoint;
    7439           0 :     if (EditorBase::IsTextNode(tmp) ||
    7440           0 :         mHTMLEditor->IsContainer(tmp)) {
    7441           0 :       endPoint = tmp->Length();
    7442             :     } else {
    7443           0 :       tmp = EditorBase::GetNodeLocation(tmp, (int32_t*)&endPoint);
    7444           0 :       endPoint++;  // want to be after this node
    7445             :     }
    7446           0 :     return aSelection->Collapse(tmp, (int32_t)endPoint);
    7447             :   } else {
    7448             :     // selection is before block.  put at start of block.
    7449           0 :     NS_ENSURE_STATE(mHTMLEditor);
    7450           0 :     nsCOMPtr<nsINode> tmp = mHTMLEditor->GetFirstEditableChild(*mNewBlock);
    7451           0 :     if (!tmp) {
    7452           0 :       tmp = mNewBlock;
    7453             :     }
    7454             :     int32_t offset;
    7455           0 :     if (EditorBase::IsTextNode(tmp) ||
    7456           0 :         mHTMLEditor->IsContainer(tmp)) {
    7457           0 :       tmp = EditorBase::GetNodeLocation(tmp, &offset);
    7458             :     }
    7459           0 :     return aSelection->Collapse(tmp, 0);
    7460             :   }
    7461             : }
    7462             : 
    7463             : void
    7464           0 : HTMLEditRules::CheckInterlinePosition(Selection& aSelection)
    7465             : {
    7466             :   // If the selection isn't collapsed, do nothing.
    7467           0 :   if (!aSelection.Collapsed()) {
    7468           0 :     return;
    7469             :   }
    7470             : 
    7471           0 :   NS_ENSURE_TRUE_VOID(mHTMLEditor);
    7472           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    7473             : 
    7474             :   // Get the (collapsed) selection location
    7475           0 :   NS_ENSURE_TRUE_VOID(aSelection.GetRangeAt(0) &&
    7476             :                       aSelection.GetRangeAt(0)->GetStartContainer());
    7477             :   OwningNonNull<nsINode> selNode =
    7478           0 :     *aSelection.GetRangeAt(0)->GetStartContainer();
    7479           0 :   int32_t selOffset = aSelection.GetRangeAt(0)->StartOffset();
    7480             : 
    7481             :   // First, let's check to see if we are after a <br>.  We take care of this
    7482             :   // special-case first so that we don't accidentally fall through into one of
    7483             :   // the other conditionals.
    7484             :   nsCOMPtr<nsIContent> node =
    7485           0 :     htmlEditor->GetPriorHTMLNode(selNode, selOffset, true);
    7486           0 :   if (node && node->IsHTMLElement(nsGkAtoms::br)) {
    7487           0 :     aSelection.SetInterlinePosition(true);
    7488           0 :     return;
    7489             :   }
    7490             : 
    7491             :   // Are we after a block?  If so try set caret to following content
    7492           0 :   node = htmlEditor->GetPriorHTMLSibling(selNode, selOffset);
    7493           0 :   if (node && IsBlockNode(*node)) {
    7494           0 :     aSelection.SetInterlinePosition(true);
    7495           0 :     return;
    7496             :   }
    7497             : 
    7498             :   // Are we before a block?  If so try set caret to prior content
    7499           0 :   node = htmlEditor->GetNextHTMLSibling(selNode, selOffset);
    7500           0 :   if (node && IsBlockNode(*node)) {
    7501           0 :     aSelection.SetInterlinePosition(false);
    7502             :   }
    7503             : }
    7504             : 
    7505             : nsresult
    7506           0 : HTMLEditRules::AdjustSelection(Selection* aSelection,
    7507             :                                nsIEditor::EDirection aAction)
    7508             : {
    7509           0 :   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
    7510             : 
    7511             :   // if the selection isn't collapsed, do nothing.
    7512             :   // moose: one thing to do instead is check for the case of
    7513             :   // only a single break selected, and collapse it.  Good thing?  Beats me.
    7514           0 :   if (!aSelection->Collapsed()) {
    7515           0 :     return NS_OK;
    7516             :   }
    7517             : 
    7518             :   // get the (collapsed) selection location
    7519           0 :   nsCOMPtr<nsINode> selNode, temp;
    7520             :   int32_t selOffset;
    7521             :   nsresult rv =
    7522           0 :     EditorBase::GetStartNodeAndOffset(aSelection,
    7523           0 :                                       getter_AddRefs(selNode), &selOffset);
    7524           0 :   NS_ENSURE_SUCCESS(rv, rv);
    7525           0 :   temp = selNode;
    7526             : 
    7527             :   // are we in an editable node?
    7528           0 :   NS_ENSURE_STATE(mHTMLEditor);
    7529           0 :   while (!mHTMLEditor->IsEditable(selNode)) {
    7530             :     // scan up the tree until we find an editable place to be
    7531           0 :     selNode = EditorBase::GetNodeLocation(temp, &selOffset);
    7532           0 :     NS_ENSURE_TRUE(selNode, NS_ERROR_FAILURE);
    7533           0 :     temp = selNode;
    7534           0 :     NS_ENSURE_STATE(mHTMLEditor);
    7535             :   }
    7536             : 
    7537             :   // make sure we aren't in an empty block - user will see no cursor.  If this
    7538             :   // is happening, put a <br> in the block if allowed.
    7539           0 :   NS_ENSURE_STATE(mHTMLEditor);
    7540           0 :   nsCOMPtr<Element> theblock = mHTMLEditor->GetBlock(*selNode);
    7541             : 
    7542           0 :   if (theblock && mHTMLEditor->IsEditable(theblock)) {
    7543             :     bool bIsEmptyNode;
    7544           0 :     NS_ENSURE_STATE(mHTMLEditor);
    7545           0 :     rv = mHTMLEditor->IsEmptyNode(theblock, &bIsEmptyNode, false, false);
    7546           0 :     NS_ENSURE_SUCCESS(rv, rv);
    7547             :     // check if br can go into the destination node
    7548           0 :     NS_ENSURE_STATE(mHTMLEditor);
    7549           0 :     if (bIsEmptyNode && mHTMLEditor->CanContainTag(*selNode, *nsGkAtoms::br)) {
    7550           0 :       NS_ENSURE_STATE(mHTMLEditor);
    7551           0 :       nsCOMPtr<Element> rootNode = mHTMLEditor->GetRoot();
    7552           0 :       NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
    7553           0 :       if (selNode == rootNode) {
    7554             :         // Our root node is completely empty. Don't add a <br> here.
    7555             :         // AfterEditInner() will add one for us when it calls
    7556             :         // CreateBogusNodeIfNeeded()!
    7557           0 :         return NS_OK;
    7558             :       }
    7559             : 
    7560             :       // we know we can skip the rest of this routine given the cirumstance
    7561           0 :       return CreateMozBR(GetAsDOMNode(selNode), selOffset);
    7562             :     }
    7563             :   }
    7564             : 
    7565             :   // are we in a text node?
    7566           0 :   if (EditorBase::IsTextNode(selNode)) {
    7567           0 :     return NS_OK; // we LIKE it when we are in a text node.  that RULZ
    7568             :   }
    7569             : 
    7570             :   // do we need to insert a special mozBR?  We do if we are:
    7571             :   // 1) prior node is in same block where selection is AND
    7572             :   // 2) prior node is a br AND
    7573             :   // 3) that br is not visible
    7574             : 
    7575           0 :   NS_ENSURE_STATE(mHTMLEditor);
    7576             :   nsCOMPtr<nsIContent> nearNode =
    7577           0 :     mHTMLEditor->GetPriorHTMLNode(selNode, selOffset);
    7578           0 :   if (nearNode) {
    7579             :     // is nearNode also a descendant of same block?
    7580           0 :     NS_ENSURE_STATE(mHTMLEditor);
    7581           0 :     nsCOMPtr<Element> block = mHTMLEditor->GetBlock(*selNode);
    7582           0 :     nsCOMPtr<Element> nearBlock = mHTMLEditor->GetBlockNodeParent(nearNode);
    7583           0 :     if (block && block == nearBlock) {
    7584           0 :       if (nearNode && TextEditUtils::IsBreak(nearNode)) {
    7585           0 :         NS_ENSURE_STATE(mHTMLEditor);
    7586           0 :         if (!mHTMLEditor->IsVisBreak(nearNode)) {
    7587             :           // need to insert special moz BR. Why?  Because if we don't
    7588             :           // the user will see no new line for the break.  Also, things
    7589             :           // like table cells won't grow in height.
    7590           0 :           nsCOMPtr<nsIDOMNode> brNode;
    7591           0 :           rv = CreateMozBR(GetAsDOMNode(selNode), selOffset,
    7592           0 :                            getter_AddRefs(brNode));
    7593           0 :           NS_ENSURE_SUCCESS(rv, rv);
    7594             :           nsCOMPtr<nsIDOMNode> brParent =
    7595           0 :             EditorBase::GetNodeLocation(brNode, &selOffset);
    7596             :           // selection stays *before* moz-br, sticking to it
    7597           0 :           aSelection->SetInterlinePosition(true);
    7598           0 :           rv = aSelection->Collapse(brParent, selOffset);
    7599           0 :           NS_ENSURE_SUCCESS(rv, rv);
    7600             :         } else {
    7601           0 :           NS_ENSURE_STATE(mHTMLEditor);
    7602             :           nsCOMPtr<nsIContent> nextNode =
    7603           0 :             mHTMLEditor->GetNextHTMLNode(nearNode, true);
    7604           0 :           if (nextNode && TextEditUtils::IsMozBR(nextNode)) {
    7605             :             // selection between br and mozbr.  make it stick to mozbr
    7606             :             // so that it will be on blank line.
    7607           0 :             aSelection->SetInterlinePosition(true);
    7608             :           }
    7609             :         }
    7610             :       }
    7611             :     }
    7612             :   }
    7613             : 
    7614             :   // we aren't in a textnode: are we adjacent to text or a break or an image?
    7615           0 :   NS_ENSURE_STATE(mHTMLEditor);
    7616           0 :   nearNode = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, true);
    7617           0 :   if (nearNode && (TextEditUtils::IsBreak(nearNode) ||
    7618           0 :                    EditorBase::IsTextNode(nearNode) ||
    7619           0 :                    HTMLEditUtils::IsImage(nearNode) ||
    7620           0 :                    nearNode->IsHTMLElement(nsGkAtoms::hr))) {
    7621             :     // this is a good place for the caret to be
    7622           0 :     return NS_OK;
    7623             :   }
    7624           0 :   NS_ENSURE_STATE(mHTMLEditor);
    7625           0 :   nearNode = mHTMLEditor->GetNextHTMLNode(selNode, selOffset, true);
    7626           0 :   if (nearNode && (TextEditUtils::IsBreak(nearNode) ||
    7627           0 :                    EditorBase::IsTextNode(nearNode) ||
    7628           0 :                    nearNode->IsAnyOfHTMLElements(nsGkAtoms::img,
    7629           0 :                                                  nsGkAtoms::hr))) {
    7630           0 :     return NS_OK; // this is a good place for the caret to be
    7631             :   }
    7632             : 
    7633             :   // look for a nearby text node.
    7634             :   // prefer the correct direction.
    7635           0 :   nsCOMPtr<nsIDOMNode> nearNodeDOM = GetAsDOMNode(nearNode);
    7636           0 :   rv = FindNearSelectableNode(GetAsDOMNode(selNode), selOffset, aAction,
    7637           0 :                               address_of(nearNodeDOM));
    7638           0 :   NS_ENSURE_SUCCESS(rv, rv);
    7639           0 :   nearNode = do_QueryInterface(nearNodeDOM);
    7640             : 
    7641           0 :   if (!nearNode) {
    7642           0 :     return NS_OK;
    7643             :   }
    7644           0 :   EditorDOMPoint pt = GetGoodSelPointForNode(*nearNode, aAction);
    7645           0 :   rv = aSelection->Collapse(pt.node, pt.offset);
    7646           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    7647           0 :     return rv;
    7648             :   }
    7649           0 :   return NS_OK;
    7650             : }
    7651             : 
    7652             : 
    7653             : nsresult
    7654           0 : HTMLEditRules::FindNearSelectableNode(nsIDOMNode* aSelNode,
    7655             :                                       int32_t aSelOffset,
    7656             :                                       nsIEditor::EDirection& aDirection,
    7657             :                                       nsCOMPtr<nsIDOMNode>* outSelectableNode)
    7658             : {
    7659           0 :   NS_ENSURE_TRUE(aSelNode && outSelectableNode, NS_ERROR_NULL_POINTER);
    7660           0 :   *outSelectableNode = nullptr;
    7661             : 
    7662           0 :   nsCOMPtr<nsIDOMNode> nearNode, curNode;
    7663           0 :   if (aDirection == nsIEditor::ePrevious) {
    7664           0 :     NS_ENSURE_STATE(mHTMLEditor);
    7665             :     nsresult rv =
    7666           0 :       mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
    7667           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    7668           0 :       return rv;
    7669             :     }
    7670             :   } else {
    7671           0 :     NS_ENSURE_STATE(mHTMLEditor);
    7672             :     nsresult rv =
    7673           0 :       mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
    7674           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    7675           0 :       return rv;
    7676             :     }
    7677             :   }
    7678             : 
    7679             :   // Try the other direction then.
    7680           0 :   if (!nearNode) {
    7681           0 :     if (aDirection == nsIEditor::ePrevious) {
    7682           0 :       aDirection = nsIEditor::eNext;
    7683             :     } else {
    7684           0 :       aDirection = nsIEditor::ePrevious;
    7685             :     }
    7686             : 
    7687           0 :     if (aDirection == nsIEditor::ePrevious) {
    7688           0 :       NS_ENSURE_STATE(mHTMLEditor);
    7689           0 :       nsresult rv = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset,
    7690           0 :                                                   address_of(nearNode));
    7691           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    7692           0 :         return rv;
    7693             :       }
    7694             :     } else {
    7695           0 :       NS_ENSURE_STATE(mHTMLEditor);
    7696           0 :       nsresult rv = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset,
    7697           0 :                                                  address_of(nearNode));
    7698           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    7699           0 :         return rv;
    7700             :       }
    7701             :     }
    7702             :   }
    7703             : 
    7704             :   // scan in the right direction until we find an eligible text node,
    7705             :   // but don't cross any breaks, images, or table elements.
    7706           0 :   while (nearNode && !(EditorBase::IsTextNode(nearNode) ||
    7707           0 :                        TextEditUtils::IsBreak(nearNode) ||
    7708           0 :                        HTMLEditUtils::IsImage(nearNode))) {
    7709           0 :     curNode = nearNode;
    7710           0 :     if (aDirection == nsIEditor::ePrevious) {
    7711           0 :       NS_ENSURE_STATE(mHTMLEditor);
    7712             :       nsresult rv =
    7713           0 :         mHTMLEditor->GetPriorHTMLNode(curNode, address_of(nearNode));
    7714           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    7715           0 :         return rv;
    7716             :       }
    7717             :     } else {
    7718           0 :       NS_ENSURE_STATE(mHTMLEditor);
    7719           0 :       nsresult rv = mHTMLEditor->GetNextHTMLNode(curNode, address_of(nearNode));
    7720           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    7721           0 :         return rv;
    7722             :       }
    7723             :     }
    7724           0 :     NS_ENSURE_STATE(mHTMLEditor);
    7725             :   }
    7726             : 
    7727           0 :   if (nearNode) {
    7728             :     // don't cross any table elements
    7729           0 :     if (InDifferentTableElements(nearNode, aSelNode)) {
    7730           0 :       return NS_OK;
    7731             :     }
    7732             : 
    7733             :     // otherwise, ok, we have found a good spot to put the selection
    7734           0 :     *outSelectableNode = do_QueryInterface(nearNode);
    7735             :   }
    7736           0 :   return NS_OK;
    7737             : }
    7738             : 
    7739             : bool
    7740           0 : HTMLEditRules::InDifferentTableElements(nsIDOMNode* aNode1,
    7741             :                                         nsIDOMNode* aNode2)
    7742             : {
    7743           0 :   nsCOMPtr<nsINode> node1 = do_QueryInterface(aNode1);
    7744           0 :   nsCOMPtr<nsINode> node2 = do_QueryInterface(aNode2);
    7745           0 :   return InDifferentTableElements(node1, node2);
    7746             : }
    7747             : 
    7748             : bool
    7749           0 : HTMLEditRules::InDifferentTableElements(nsINode* aNode1,
    7750             :                                         nsINode* aNode2)
    7751             : {
    7752           0 :   MOZ_ASSERT(aNode1 && aNode2);
    7753             : 
    7754           0 :   while (aNode1 && !HTMLEditUtils::IsTableElement(aNode1)) {
    7755           0 :     aNode1 = aNode1->GetParentNode();
    7756             :   }
    7757             : 
    7758           0 :   while (aNode2 && !HTMLEditUtils::IsTableElement(aNode2)) {
    7759           0 :     aNode2 = aNode2->GetParentNode();
    7760             :   }
    7761             : 
    7762           0 :   return aNode1 != aNode2;
    7763             : }
    7764             : 
    7765             : 
    7766             : nsresult
    7767           0 : HTMLEditRules::RemoveEmptyNodes()
    7768             : {
    7769           0 :   NS_ENSURE_STATE(mHTMLEditor);
    7770           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    7771             : 
    7772             :   // Some general notes on the algorithm used here: the goal is to examine all
    7773             :   // the nodes in mDocChangeRange, and remove the empty ones.  We do this by
    7774             :   // using a content iterator to traverse all the nodes in the range, and
    7775             :   // placing the empty nodes into an array.  After finishing the iteration, we
    7776             :   // delete the empty nodes in the array.  (They cannot be deleted as we find
    7777             :   // them because that would invalidate the iterator.)
    7778             :   //
    7779             :   // Since checking to see if a node is empty can be costly for nodes with many
    7780             :   // descendants, there are some optimizations made.  I rely on the fact that
    7781             :   // the iterator is post-order: it will visit children of a node before
    7782             :   // visiting the parent node.  So if I find that a child node is not empty, I
    7783             :   // know that its parent is not empty without even checking.  So I put the
    7784             :   // parent on a "skipList" which is just a voidArray of nodes I can skip the
    7785             :   // empty check on.  If I encounter a node on the skiplist, i skip the
    7786             :   // processing for that node and replace its slot in the skiplist with that
    7787             :   // node's parent.
    7788             :   //
    7789             :   // An interesting idea is to go ahead and regard parent nodes that are NOT on
    7790             :   // the skiplist as being empty (without even doing the IsEmptyNode check) on
    7791             :   // the theory that if they weren't empty, we would have encountered a
    7792             :   // non-empty child earlier and thus put this parent node on the skiplist.
    7793             :   //
    7794             :   // Unfortunately I can't use that strategy here, because the range may
    7795             :   // include some children of a node while excluding others.  Thus I could find
    7796             :   // all the _examined_ children empty, but still not have an empty parent.
    7797             : 
    7798             :   // need an iterator
    7799           0 :   nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
    7800             : 
    7801           0 :   nsresult rv = iter->Init(mDocChangeRange);
    7802           0 :   NS_ENSURE_SUCCESS(rv, rv);
    7803             : 
    7804           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfEmptyNodes, arrayOfEmptyCites, skipList;
    7805             : 
    7806             :   // Check for empty nodes
    7807           0 :   while (!iter->IsDone()) {
    7808           0 :     OwningNonNull<nsINode> node = *iter->GetCurrentNode();
    7809             : 
    7810           0 :     nsCOMPtr<nsINode> parent = node->GetParentNode();
    7811             : 
    7812           0 :     size_t idx = skipList.IndexOf(node);
    7813           0 :     if (idx != skipList.NoIndex) {
    7814             :       // This node is on our skip list.  Skip processing for this node, and
    7815             :       // replace its value in the skip list with the value of its parent
    7816           0 :       if (parent) {
    7817           0 :         skipList[idx] = parent;
    7818             :       }
    7819             :     } else {
    7820           0 :       bool bIsCandidate = false;
    7821           0 :       bool bIsEmptyNode = false;
    7822           0 :       bool bIsMailCite = false;
    7823             : 
    7824           0 :       if (node->IsElement()) {
    7825           0 :         if (node->IsHTMLElement(nsGkAtoms::body)) {
    7826             :           // Don't delete the body
    7827           0 :         } else if ((bIsMailCite = HTMLEditUtils::IsMailCite(node)) ||
    7828           0 :                    node->IsHTMLElement(nsGkAtoms::a) ||
    7829           0 :                    HTMLEditUtils::IsInlineStyle(node) ||
    7830           0 :                    HTMLEditUtils::IsList(node) ||
    7831           0 :                    node->IsHTMLElement(nsGkAtoms::div)) {
    7832             :           // Only consider certain nodes to be empty for purposes of removal
    7833           0 :           bIsCandidate = true;
    7834           0 :         } else if (HTMLEditUtils::IsFormatNode(node) ||
    7835           0 :                    HTMLEditUtils::IsListItem(node) ||
    7836           0 :                    node->IsHTMLElement(nsGkAtoms::blockquote)) {
    7837             :           // These node types are candidates if selection is not in them.  If
    7838             :           // it is one of these, don't delete if selection inside.  This is so
    7839             :           // we can create empty headings, etc., for the user to type into.
    7840             :           bool bIsSelInNode;
    7841           0 :           rv = SelectionEndpointInNode(node, &bIsSelInNode);
    7842           0 :           NS_ENSURE_SUCCESS(rv, rv);
    7843           0 :           if (!bIsSelInNode) {
    7844           0 :             bIsCandidate = true;
    7845             :           }
    7846             :         }
    7847             :       }
    7848             : 
    7849           0 :       if (bIsCandidate) {
    7850             :         // We delete mailcites even if they have a solo br in them.  Other
    7851             :         // nodes we require to be empty.
    7852           0 :         rv = htmlEditor->IsEmptyNode(node->AsDOMNode(), &bIsEmptyNode,
    7853           0 :                                      bIsMailCite, true);
    7854           0 :         NS_ENSURE_SUCCESS(rv, rv);
    7855           0 :         if (bIsEmptyNode) {
    7856           0 :           if (bIsMailCite) {
    7857             :             // mailcites go on a separate list from other empty nodes
    7858           0 :             arrayOfEmptyCites.AppendElement(*node);
    7859             :           } else {
    7860           0 :             arrayOfEmptyNodes.AppendElement(*node);
    7861             :           }
    7862             :         }
    7863             :       }
    7864             : 
    7865           0 :       if (!bIsEmptyNode && parent) {
    7866             :         // put parent on skip list
    7867           0 :         skipList.AppendElement(*parent);
    7868             :       }
    7869             :     }
    7870             : 
    7871           0 :     iter->Next();
    7872             :   }
    7873             : 
    7874             :   // now delete the empty nodes
    7875           0 :   for (auto& delNode : arrayOfEmptyNodes) {
    7876           0 :     if (htmlEditor->IsModifiableNode(delNode)) {
    7877           0 :       rv = htmlEditor->DeleteNode(delNode);
    7878           0 :       NS_ENSURE_SUCCESS(rv, rv);
    7879             :     }
    7880             :   }
    7881             : 
    7882             :   // Now delete the empty mailcites.  This is a separate step because we want
    7883             :   // to pull out any br's and preserve them.
    7884           0 :   for (auto& delNode : arrayOfEmptyCites) {
    7885             :     bool bIsEmptyNode;
    7886           0 :     rv = htmlEditor->IsEmptyNode(delNode, &bIsEmptyNode, false, true);
    7887           0 :     NS_ENSURE_SUCCESS(rv, rv);
    7888           0 :     if (!bIsEmptyNode) {
    7889             :       // We are deleting a cite that has just a br.  We want to delete cite,
    7890             :       // but preserve br.
    7891           0 :       nsCOMPtr<nsINode> parent = delNode->GetParentNode();
    7892           0 :       int32_t offset = parent ? parent->IndexOf(delNode) : -1;
    7893           0 :       nsCOMPtr<Element> br = htmlEditor->CreateBR(parent, offset);
    7894           0 :       NS_ENSURE_STATE(br);
    7895             :     }
    7896           0 :     rv = htmlEditor->DeleteNode(delNode);
    7897           0 :     NS_ENSURE_SUCCESS(rv, rv);
    7898             :   }
    7899             : 
    7900           0 :   return NS_OK;
    7901             : }
    7902             : 
    7903             : nsresult
    7904           0 : HTMLEditRules::SelectionEndpointInNode(nsINode* aNode,
    7905             :                                        bool* aResult)
    7906             : {
    7907           0 :   NS_ENSURE_TRUE(aNode && aResult, NS_ERROR_NULL_POINTER);
    7908             : 
    7909           0 :   nsIDOMNode* node = aNode->AsDOMNode();
    7910             : 
    7911           0 :   *aResult = false;
    7912             : 
    7913           0 :   NS_ENSURE_STATE(mHTMLEditor);
    7914           0 :   RefPtr<Selection> selection = mHTMLEditor->GetSelection();
    7915           0 :   NS_ENSURE_STATE(selection);
    7916             : 
    7917           0 :   uint32_t rangeCount = selection->RangeCount();
    7918           0 :   for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
    7919           0 :     RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
    7920           0 :     nsCOMPtr<nsIDOMNode> startContainer, endContainer;
    7921           0 :     range->GetStartContainer(getter_AddRefs(startContainer));
    7922           0 :     if (startContainer) {
    7923           0 :       if (node == startContainer) {
    7924           0 :         *aResult = true;
    7925           0 :         return NS_OK;
    7926             :       }
    7927           0 :       if (EditorUtils::IsDescendantOf(startContainer, node)) {
    7928           0 :         *aResult = true;
    7929           0 :         return NS_OK;
    7930             :       }
    7931             :     }
    7932           0 :     range->GetEndContainer(getter_AddRefs(endContainer));
    7933           0 :     if (startContainer == endContainer) {
    7934           0 :       continue;
    7935             :     }
    7936           0 :     if (endContainer) {
    7937           0 :       if (node == endContainer) {
    7938           0 :         *aResult = true;
    7939           0 :         return NS_OK;
    7940             :       }
    7941           0 :       if (EditorUtils::IsDescendantOf(endContainer, node)) {
    7942           0 :         *aResult = true;
    7943           0 :         return NS_OK;
    7944             :       }
    7945             :     }
    7946             :   }
    7947           0 :   return NS_OK;
    7948             : }
    7949             : 
    7950             : /**
    7951             :  * IsEmptyInline: Return true if aNode is an empty inline container
    7952             :  */
    7953             : bool
    7954           0 : HTMLEditRules::IsEmptyInline(nsINode& aNode)
    7955             : {
    7956           0 :   NS_ENSURE_TRUE(mHTMLEditor, false);
    7957           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    7958             : 
    7959           0 :   if (IsInlineNode(aNode) && htmlEditor->IsContainer(&aNode)) {
    7960           0 :     bool isEmpty = true;
    7961           0 :     htmlEditor->IsEmptyNode(&aNode, &isEmpty);
    7962           0 :     return isEmpty;
    7963             :   }
    7964           0 :   return false;
    7965             : }
    7966             : 
    7967             : 
    7968             : bool
    7969           0 : HTMLEditRules::ListIsEmptyLine(nsTArray<OwningNonNull<nsINode>>& aArrayOfNodes)
    7970             : {
    7971             :   // We have a list of nodes which we are candidates for being moved into a new
    7972             :   // block.  Determine if it's anything more than a blank line.  Look for
    7973             :   // editable content above and beyond one single BR.
    7974           0 :   NS_ENSURE_TRUE(aArrayOfNodes.Length(), true);
    7975             : 
    7976           0 :   NS_ENSURE_TRUE(mHTMLEditor, false);
    7977           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    7978             : 
    7979           0 :   int32_t brCount = 0;
    7980             : 
    7981           0 :   for (auto& node : aArrayOfNodes) {
    7982           0 :     if (!htmlEditor->IsEditable(node)) {
    7983           0 :       continue;
    7984             :     }
    7985           0 :     if (TextEditUtils::IsBreak(node)) {
    7986             :       // First break doesn't count
    7987           0 :       if (brCount) {
    7988           0 :         return false;
    7989             :       }
    7990           0 :       brCount++;
    7991           0 :     } else if (IsEmptyInline(node)) {
    7992             :       // Empty inline, keep looking
    7993             :     } else {
    7994           0 :       return false;
    7995             :     }
    7996             :   }
    7997           0 :   return true;
    7998             : }
    7999             : 
    8000             : 
    8001             : nsresult
    8002           0 : HTMLEditRules::PopListItem(nsIContent& aListItem,
    8003             :                            bool* aOutOfList)
    8004             : {
    8005             :   // init out params
    8006           0 :   if (aOutOfList) {
    8007           0 :     *aOutOfList = false;
    8008             :   }
    8009             : 
    8010           0 :   nsCOMPtr<nsIContent> kungFuDeathGrip(&aListItem);
    8011             :   Unused << kungFuDeathGrip;
    8012             : 
    8013           0 :   nsCOMPtr<nsINode> curParent = aListItem.GetParentNode();
    8014           0 :   if (NS_WARN_IF(!curParent)) {
    8015           0 :     return NS_ERROR_FAILURE;
    8016             :   }
    8017           0 :   int32_t offset = curParent->IndexOf(&aListItem);
    8018             : 
    8019           0 :   if (!HTMLEditUtils::IsListItem(&aListItem)) {
    8020           0 :     return NS_ERROR_FAILURE;
    8021             :   }
    8022             : 
    8023             :   // if it's first or last list item, don't need to split the list
    8024             :   // otherwise we do.
    8025           0 :   nsCOMPtr<nsINode> curParPar = curParent->GetParentNode();
    8026           0 :   int32_t parOffset = curParPar ? curParPar->IndexOf(curParent) : -1;
    8027             : 
    8028           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8029           0 :   bool bIsFirstListItem = mHTMLEditor->IsFirstEditableChild(&aListItem);
    8030             : 
    8031           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8032           0 :   bool bIsLastListItem = mHTMLEditor->IsLastEditableChild(&aListItem);
    8033             : 
    8034           0 :   if (!bIsFirstListItem && !bIsLastListItem) {
    8035             :     // split the list
    8036           0 :     ErrorResult rv;
    8037           0 :     NS_ENSURE_STATE(mHTMLEditor);
    8038           0 :     mHTMLEditor->SplitNode(*curParent->AsContent(), offset, rv);
    8039           0 :     if (NS_WARN_IF(rv.Failed())) {
    8040           0 :       return rv.StealNSResult();
    8041             :     }
    8042             :   }
    8043             : 
    8044           0 :   if (!bIsFirstListItem) {
    8045           0 :     parOffset++;
    8046             :   }
    8047             : 
    8048           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8049           0 :   nsresult rv = mHTMLEditor->MoveNode(&aListItem, curParPar, parOffset);
    8050           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8051             : 
    8052             :   // unwrap list item contents if they are no longer in a list
    8053           0 :   if (!HTMLEditUtils::IsList(curParPar) &&
    8054           0 :       HTMLEditUtils::IsListItem(&aListItem)) {
    8055           0 :     NS_ENSURE_STATE(mHTMLEditor);
    8056           0 :     rv = mHTMLEditor->RemoveBlockContainer(*aListItem.AsElement());
    8057           0 :     NS_ENSURE_SUCCESS(rv, rv);
    8058           0 :     if (aOutOfList) {
    8059           0 :       *aOutOfList = true;
    8060             :     }
    8061             :   }
    8062           0 :   return NS_OK;
    8063             : }
    8064             : 
    8065             : nsresult
    8066           0 : HTMLEditRules::RemoveListStructure(Element& aList)
    8067             : {
    8068           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8069           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    8070           0 :   while (aList.GetFirstChild()) {
    8071           0 :     OwningNonNull<nsIContent> child = *aList.GetFirstChild();
    8072             : 
    8073           0 :     if (HTMLEditUtils::IsListItem(child)) {
    8074             :       bool isOutOfList;
    8075             :       // Keep popping it out until it's not in a list anymore
    8076           0 :       do {
    8077           0 :         nsresult rv = PopListItem(child, &isOutOfList);
    8078           0 :         NS_ENSURE_SUCCESS(rv, rv);
    8079           0 :       } while (!isOutOfList);
    8080           0 :     } else if (HTMLEditUtils::IsList(child)) {
    8081           0 :       nsresult rv = RemoveListStructure(*child->AsElement());
    8082           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8083             :     } else {
    8084             :       // Delete any non-list items for now
    8085           0 :       nsresult rv = htmlEditor->DeleteNode(child);
    8086           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8087             :     }
    8088             :   }
    8089             : 
    8090             :   // Delete the now-empty list
    8091           0 :   nsresult rv = htmlEditor->RemoveBlockContainer(aList);
    8092           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8093             : 
    8094           0 :   return NS_OK;
    8095             : }
    8096             : 
    8097             : nsresult
    8098           0 : HTMLEditRules::ConfirmSelectionInBody()
    8099             : {
    8100             :   // get the body
    8101           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8102           0 :   RefPtr<Element> rootElement = mHTMLEditor->GetRoot();
    8103           0 :   if (NS_WARN_IF(!rootElement)) {
    8104           0 :     return NS_ERROR_UNEXPECTED;
    8105             :   }
    8106             : 
    8107             :   // get the selection
    8108           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8109           0 :   RefPtr<Selection> selection = mHTMLEditor->GetSelection();
    8110           0 :   if (NS_WARN_IF(!selection)) {
    8111           0 :     return NS_ERROR_UNEXPECTED;
    8112             :   }
    8113             : 
    8114             :   // get the selection start location
    8115           0 :   nsCOMPtr<nsINode> selNode;
    8116             :   int32_t selOffset;
    8117             :   nsresult rv =
    8118           0 :     EditorBase::GetStartNodeAndOffset(selection,
    8119           0 :                                       getter_AddRefs(selNode), &selOffset);
    8120           0 :   if (NS_FAILED(rv)) {
    8121           0 :     return rv;
    8122             :   }
    8123             : 
    8124           0 :   nsINode* temp = selNode;
    8125             : 
    8126             :   // check that selNode is inside body
    8127           0 :   while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
    8128           0 :     temp = temp->GetParentNode();
    8129             :   }
    8130             : 
    8131             :   // if we aren't in the body, force the issue
    8132           0 :   if (!temp) {
    8133             : //    uncomment this to see when we get bad selections
    8134             : //    NS_NOTREACHED("selection not in body");
    8135           0 :     selection->Collapse(rootElement, 0);
    8136           0 :     return NS_OK;
    8137             :   }
    8138             : 
    8139             :   // get the selection end location
    8140           0 :   rv = EditorBase::GetEndNodeAndOffset(selection,
    8141           0 :                                        getter_AddRefs(selNode), &selOffset);
    8142           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8143           0 :   temp = selNode;
    8144             : 
    8145             :   // check that selNode is inside body
    8146           0 :   while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
    8147           0 :     temp = temp->GetParentNode();
    8148             :   }
    8149             : 
    8150             :   // if we aren't in the body, force the issue
    8151           0 :   if (!temp) {
    8152             : //    uncomment this to see when we get bad selections
    8153             : //    NS_NOTREACHED("selection not in body");
    8154           0 :     selection->Collapse(rootElement, 0);
    8155             :   }
    8156             : 
    8157           0 :   return NS_OK;
    8158             : }
    8159             : 
    8160             : nsresult
    8161           0 : HTMLEditRules::UpdateDocChangeRange(nsRange* aRange)
    8162             : {
    8163             :   // first make sure aRange is in the document.  It might not be if
    8164             :   // portions of our editting action involved manipulating nodes
    8165             :   // prior to placing them in the document (e.g., populating a list item
    8166             :   // before placing it in its list)
    8167           0 :   nsCOMPtr<nsIDOMNode> startNode;
    8168           0 :   nsresult rv = aRange->GetStartContainer(getter_AddRefs(startNode));
    8169           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8170           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8171           0 :   if (!mHTMLEditor->IsDescendantOfRoot(startNode)) {
    8172             :     // just return - we don't need to adjust mDocChangeRange in this case
    8173           0 :     return NS_OK;
    8174             :   }
    8175             : 
    8176           0 :   if (!mDocChangeRange) {
    8177             :     // clone aRange.
    8178           0 :     mDocChangeRange = aRange->CloneRange();
    8179             :   } else {
    8180             :     int16_t result;
    8181             : 
    8182             :     // compare starts of ranges
    8183           0 :     rv = mDocChangeRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START,
    8184           0 :                                                 aRange, &result);
    8185           0 :     if (rv == NS_ERROR_NOT_INITIALIZED) {
    8186             :       // This will happen is mDocChangeRange is non-null, but the range is
    8187             :       // uninitialized. In this case we'll set the start to aRange start.
    8188             :       // The same test won't be needed further down since after we've set
    8189             :       // the start the range will be collapsed to that point.
    8190           0 :       result = 1;
    8191           0 :       rv = NS_OK;
    8192             :     }
    8193           0 :     NS_ENSURE_SUCCESS(rv, rv);
    8194             :     // Positive result means mDocChangeRange start is after aRange start.
    8195           0 :     if (result > 0) {
    8196             :       int32_t startOffset;
    8197           0 :       rv = aRange->GetStartOffset(&startOffset);
    8198           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8199           0 :       rv = mDocChangeRange->SetStart(startNode, startOffset);
    8200           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8201             :     }
    8202             : 
    8203             :     // compare ends of ranges
    8204           0 :     rv = mDocChangeRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END,
    8205           0 :                                                 aRange, &result);
    8206           0 :     NS_ENSURE_SUCCESS(rv, rv);
    8207             :     // Negative result means mDocChangeRange end is before aRange end.
    8208           0 :     if (result < 0) {
    8209           0 :       nsCOMPtr<nsIDOMNode> endNode;
    8210             :       int32_t endOffset;
    8211           0 :       rv = aRange->GetEndContainer(getter_AddRefs(endNode));
    8212           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8213           0 :       rv = aRange->GetEndOffset(&endOffset);
    8214           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8215           0 :       rv = mDocChangeRange->SetEnd(endNode, endOffset);
    8216           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8217             :     }
    8218             :   }
    8219           0 :   return NS_OK;
    8220             : }
    8221             : 
    8222             : nsresult
    8223           0 : HTMLEditRules::InsertMozBRIfNeeded(nsINode& aNode)
    8224             : {
    8225           0 :   if (!IsBlockNode(aNode)) {
    8226           0 :     return NS_OK;
    8227             :   }
    8228             : 
    8229             :   bool isEmpty;
    8230           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8231           0 :   nsresult rv = mHTMLEditor->IsEmptyNode(&aNode, &isEmpty);
    8232           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8233           0 :   if (!isEmpty) {
    8234           0 :     return NS_OK;
    8235             :   }
    8236             : 
    8237           0 :   return CreateMozBR(aNode.AsDOMNode(), 0);
    8238             : }
    8239             : 
    8240             : NS_IMETHODIMP
    8241           0 : HTMLEditRules::WillCreateNode(const nsAString& aTag,
    8242             :                               nsIDOMNode* aParent,
    8243             :                               int32_t aPosition)
    8244             : {
    8245           0 :   return NS_OK;
    8246             : }
    8247             : 
    8248             : NS_IMETHODIMP
    8249           0 : HTMLEditRules::DidCreateNode(const nsAString& aTag,
    8250             :                              nsIDOMNode* aNode,
    8251             :                              nsIDOMNode* aParent,
    8252             :                              int32_t aPosition,
    8253             :                              nsresult aResult)
    8254             : {
    8255           0 :   if (!mListenerEnabled) {
    8256           0 :     return NS_OK;
    8257             :   }
    8258             :   // assumption that Join keeps the righthand node
    8259           0 :   nsresult rv = mUtilRange->SelectNode(aNode);
    8260           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8261           0 :   return UpdateDocChangeRange(mUtilRange);
    8262             : }
    8263             : 
    8264             : NS_IMETHODIMP
    8265           0 : HTMLEditRules::WillInsertNode(nsIDOMNode* aNode,
    8266             :                               nsIDOMNode* aParent,
    8267             :                               int32_t aPosition)
    8268             : {
    8269           0 :   return NS_OK;
    8270             : }
    8271             : 
    8272             : NS_IMETHODIMP
    8273           0 : HTMLEditRules::DidInsertNode(nsIDOMNode* aNode,
    8274             :                              nsIDOMNode* aParent,
    8275             :                              int32_t aPosition,
    8276             :                              nsresult aResult)
    8277             : {
    8278           0 :   if (!mListenerEnabled) {
    8279           0 :     return NS_OK;
    8280             :   }
    8281           0 :   nsresult rv = mUtilRange->SelectNode(aNode);
    8282           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8283           0 :   return UpdateDocChangeRange(mUtilRange);
    8284             : }
    8285             : 
    8286             : NS_IMETHODIMP
    8287           0 : HTMLEditRules::WillDeleteNode(nsIDOMNode* aChild)
    8288             : {
    8289           0 :   if (!mListenerEnabled) {
    8290           0 :     return NS_OK;
    8291             :   }
    8292           0 :   nsresult rv = mUtilRange->SelectNode(aChild);
    8293           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8294           0 :   return UpdateDocChangeRange(mUtilRange);
    8295             : }
    8296             : 
    8297             : NS_IMETHODIMP
    8298           0 : HTMLEditRules::DidDeleteNode(nsIDOMNode* aChild,
    8299             :                              nsresult aResult)
    8300             : {
    8301           0 :   return NS_OK;
    8302             : }
    8303             : 
    8304             : NS_IMETHODIMP
    8305           0 : HTMLEditRules::WillSplitNode(nsIDOMNode* aExistingRightNode,
    8306             :                              int32_t aOffset)
    8307             : {
    8308           0 :   return NS_OK;
    8309             : }
    8310             : 
    8311             : NS_IMETHODIMP
    8312           0 : HTMLEditRules::DidSplitNode(nsIDOMNode* aExistingRightNode,
    8313             :                             int32_t aOffset,
    8314             :                             nsIDOMNode* aNewLeftNode,
    8315             :                             nsresult aResult)
    8316             : {
    8317           0 :   if (!mListenerEnabled) {
    8318           0 :     return NS_OK;
    8319             :   }
    8320           0 :   nsCOMPtr<nsINode> newLeftNode = do_QueryInterface(aNewLeftNode);
    8321           0 :   nsCOMPtr<nsINode> existingRightNode = do_QueryInterface(aExistingRightNode);
    8322           0 :   nsresult rv = mUtilRange->SetStartAndEnd(newLeftNode, 0,
    8323           0 :                                            existingRightNode, 0);
    8324           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    8325           0 :     return rv;
    8326             :   }
    8327           0 :   return UpdateDocChangeRange(mUtilRange);
    8328             : }
    8329             : 
    8330             : NS_IMETHODIMP
    8331           0 : HTMLEditRules::WillJoinNodes(nsIDOMNode* aLeftNode,
    8332             :                              nsIDOMNode* aRightNode,
    8333             :                              nsIDOMNode* aParent)
    8334             : {
    8335           0 :   if (!mListenerEnabled) {
    8336           0 :     return NS_OK;
    8337             :   }
    8338             :   // remember split point
    8339           0 :   return EditorBase::GetLengthOfDOMNode(aLeftNode, mJoinOffset);
    8340             : }
    8341             : 
    8342             : NS_IMETHODIMP
    8343           0 : HTMLEditRules::DidJoinNodes(nsIDOMNode* aLeftNode,
    8344             :                             nsIDOMNode* aRightNode,
    8345             :                             nsIDOMNode* aParent,
    8346             :                             nsresult aResult)
    8347             : {
    8348           0 :   if (!mListenerEnabled) {
    8349           0 :     return NS_OK;
    8350             :   }
    8351           0 :   nsCOMPtr<nsINode> rightNode = do_QueryInterface(aRightNode);
    8352             :   // assumption that Join keeps the righthand node
    8353           0 :   nsresult rv = mUtilRange->CollapseTo(rightNode, mJoinOffset);
    8354           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    8355           0 :     return rv;
    8356             :   }
    8357           0 :   return UpdateDocChangeRange(mUtilRange);
    8358             : }
    8359             : 
    8360             : NS_IMETHODIMP
    8361           0 : HTMLEditRules::WillInsertText(nsIDOMCharacterData* aTextNode,
    8362             :                               int32_t aOffset,
    8363             :                               const nsAString& aString)
    8364             : {
    8365           0 :   return NS_OK;
    8366             : }
    8367             : 
    8368             : NS_IMETHODIMP
    8369           0 : HTMLEditRules::DidInsertText(nsIDOMCharacterData* aTextNode,
    8370             :                              int32_t aOffset,
    8371             :                              const nsAString& aString,
    8372             :                              nsresult aResult)
    8373             : {
    8374           0 :   if (!mListenerEnabled) {
    8375           0 :     return NS_OK;
    8376             :   }
    8377           0 :   int32_t length = aString.Length();
    8378           0 :   nsCOMPtr<nsINode> theNode = do_QueryInterface(aTextNode);
    8379           0 :   nsresult rv = mUtilRange->SetStartAndEnd(theNode, aOffset,
    8380           0 :                                            theNode, aOffset + length);
    8381           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    8382           0 :     return rv;
    8383             :   }
    8384           0 :   return UpdateDocChangeRange(mUtilRange);
    8385             : }
    8386             : 
    8387             : NS_IMETHODIMP
    8388           0 : HTMLEditRules::WillDeleteText(nsIDOMCharacterData* aTextNode,
    8389             :                               int32_t aOffset,
    8390             :                               int32_t aLength)
    8391             : {
    8392           0 :   return NS_OK;
    8393             : }
    8394             : 
    8395             : NS_IMETHODIMP
    8396           0 : HTMLEditRules::DidDeleteText(nsIDOMCharacterData* aTextNode,
    8397             :                              int32_t aOffset,
    8398             :                              int32_t aLength,
    8399             :                              nsresult aResult)
    8400             : {
    8401           0 :   if (!mListenerEnabled) {
    8402           0 :     return NS_OK;
    8403             :   }
    8404           0 :   nsCOMPtr<nsINode> theNode = do_QueryInterface(aTextNode);
    8405           0 :   nsresult rv = mUtilRange->CollapseTo(theNode, aOffset);
    8406           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    8407           0 :     return rv;
    8408             :   }
    8409           0 :   return UpdateDocChangeRange(mUtilRange);
    8410             : }
    8411             : 
    8412             : NS_IMETHODIMP
    8413           0 : HTMLEditRules::WillDeleteSelection(nsISelection* aSelection)
    8414             : {
    8415           0 :   if (!mListenerEnabled) {
    8416           0 :     return NS_OK;
    8417             :   }
    8418           0 :   if (NS_WARN_IF(!aSelection)) {
    8419           0 :     return NS_ERROR_INVALID_ARG;
    8420             :   }
    8421           0 :   RefPtr<Selection> selection = aSelection->AsSelection();
    8422             :   // get the (collapsed) selection location
    8423           0 :   nsCOMPtr<nsINode> startNode;
    8424             :   int32_t startOffset;
    8425             :   nsresult rv =
    8426           0 :     EditorBase::GetStartNodeAndOffset(selection,
    8427           0 :                                       getter_AddRefs(startNode), &startOffset);
    8428           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    8429           0 :     return rv;
    8430             :   }
    8431           0 :   nsCOMPtr<nsINode> endNode;
    8432             :   int32_t endOffset;
    8433           0 :   rv = EditorBase::GetEndNodeAndOffset(selection,
    8434           0 :                                        getter_AddRefs(endNode), &endOffset);
    8435           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    8436           0 :     return rv;
    8437             :   }
    8438           0 :   rv = mUtilRange->SetStartAndEnd(startNode, startOffset, endNode, endOffset);
    8439           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    8440           0 :     return rv;
    8441             :   }
    8442           0 :   return UpdateDocChangeRange(mUtilRange);
    8443             : }
    8444             : 
    8445             : NS_IMETHODIMP
    8446           0 : HTMLEditRules::DidDeleteSelection(nsISelection *aSelection)
    8447             : {
    8448           0 :   return NS_OK;
    8449             : }
    8450             : 
    8451             : // Let's remove all alignment hints in the children of aNode; it can
    8452             : // be an ALIGN attribute (in case we just remove it) or a CENTER
    8453             : // element (here we have to remove the container and keep its
    8454             : // children). We break on tables and don't look at their children.
    8455             : nsresult
    8456           0 : HTMLEditRules::RemoveAlignment(nsINode& aNode,
    8457             :                                const nsAString& aAlignType,
    8458             :                                bool aChildrenOnly)
    8459             : {
    8460           0 :   if (EditorBase::IsTextNode(&aNode) || HTMLEditUtils::IsTable(&aNode)) {
    8461           0 :     return NS_OK;
    8462             :   }
    8463             : 
    8464           0 :   nsCOMPtr<nsINode> child, tmp;
    8465           0 :   if (aChildrenOnly) {
    8466           0 :     child = aNode.GetFirstChild();
    8467             :   } else {
    8468           0 :     child = &aNode;
    8469             :   }
    8470           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8471           0 :   bool useCSS = mHTMLEditor->IsCSSEnabled();
    8472             : 
    8473           0 :   while (child) {
    8474           0 :     if (aChildrenOnly) {
    8475             :       // get the next sibling right now because we could have to remove child
    8476           0 :       tmp = child->GetNextSibling();
    8477             :     } else {
    8478           0 :       tmp = nullptr;
    8479             :     }
    8480             : 
    8481           0 :     if (child->IsHTMLElement(nsGkAtoms::center)) {
    8482             :       // the current node is a CENTER element
    8483             :       // first remove children's alignment
    8484           0 :       nsresult rv = RemoveAlignment(*child, aAlignType, true);
    8485           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8486             : 
    8487             :       // we may have to insert BRs in first and last position of element's children
    8488             :       // if the nodes before/after are not blocks and not BRs
    8489           0 :       rv = MakeSureElemStartsOrEndsOnCR(*child);
    8490           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8491             : 
    8492             :       // now remove the CENTER container
    8493           0 :       NS_ENSURE_STATE(mHTMLEditor);
    8494           0 :       rv = mHTMLEditor->RemoveContainer(child->AsElement());
    8495           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8496           0 :     } else if (IsBlockNode(*child) || child->IsHTMLElement(nsGkAtoms::hr)) {
    8497             :       // the current node is a block element
    8498           0 :       if (HTMLEditUtils::SupportsAlignAttr(*child)) {
    8499             :         // remove the ALIGN attribute if this element can have it
    8500           0 :         NS_ENSURE_STATE(mHTMLEditor);
    8501           0 :         nsresult rv = mHTMLEditor->RemoveAttribute(child->AsElement(),
    8502           0 :                                                    nsGkAtoms::align);
    8503           0 :         NS_ENSURE_SUCCESS(rv, rv);
    8504             :       }
    8505           0 :       if (useCSS) {
    8506           0 :         if (child->IsAnyOfHTMLElements(nsGkAtoms::table, nsGkAtoms::hr)) {
    8507           0 :           NS_ENSURE_STATE(mHTMLEditor);
    8508             :           nsresult rv =
    8509           0 :             mHTMLEditor->SetAttributeOrEquivalent(child->AsElement(),
    8510             :                                                   nsGkAtoms::align,
    8511           0 :                                                   aAlignType, false);
    8512           0 :           if (NS_WARN_IF(NS_FAILED(rv))) {
    8513           0 :             return rv;
    8514             :           }
    8515             :         } else {
    8516           0 :           nsAutoString dummyCssValue;
    8517           0 :           NS_ENSURE_STATE(mHTMLEditor);
    8518           0 :           nsresult rv = mHTMLEditor->mCSSEditUtils->RemoveCSSInlineStyle(
    8519             :                                                       *child,
    8520             :                                                       nsGkAtoms::textAlign,
    8521           0 :                                                       dummyCssValue);
    8522           0 :           if (NS_WARN_IF(NS_FAILED(rv))) {
    8523           0 :             return rv;
    8524             :           }
    8525             :         }
    8526             :       }
    8527           0 :       if (!child->IsHTMLElement(nsGkAtoms::table)) {
    8528             :         // unless this is a table, look at children
    8529           0 :         nsresult rv = RemoveAlignment(*child, aAlignType, true);
    8530           0 :         NS_ENSURE_SUCCESS(rv, rv);
    8531             :       }
    8532             :     }
    8533           0 :     child = tmp;
    8534             :   }
    8535           0 :   return NS_OK;
    8536             : }
    8537             : 
    8538             : // Let's insert a BR as first (resp. last) child of aNode if its
    8539             : // first (resp. last) child is not a block nor a BR, and if the
    8540             : // previous (resp. next) sibling is not a block nor a BR
    8541             : nsresult
    8542           0 : HTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsINode& aNode,
    8543             :                                             bool aStarts)
    8544             : {
    8545           0 :   nsCOMPtr<nsINode> child;
    8546           0 :   if (aStarts) {
    8547           0 :     NS_ENSURE_STATE(mHTMLEditor);
    8548           0 :     child = mHTMLEditor->GetFirstEditableChild(aNode);
    8549             :   } else {
    8550           0 :     NS_ENSURE_STATE(mHTMLEditor);
    8551           0 :     child = mHTMLEditor->GetLastEditableChild(aNode);
    8552             :   }
    8553           0 :   NS_ENSURE_TRUE(child, NS_OK);
    8554           0 :   bool foundCR = false;
    8555           0 :   if (IsBlockNode(*child) || child->IsHTMLElement(nsGkAtoms::br)) {
    8556           0 :     foundCR = true;
    8557             :   } else {
    8558           0 :     nsCOMPtr<nsINode> sibling;
    8559           0 :     if (aStarts) {
    8560           0 :       NS_ENSURE_STATE(mHTMLEditor);
    8561           0 :       sibling = mHTMLEditor->GetPriorHTMLSibling(&aNode);
    8562             :     } else {
    8563           0 :       NS_ENSURE_STATE(mHTMLEditor);
    8564           0 :       sibling = mHTMLEditor->GetNextHTMLSibling(&aNode);
    8565             :     }
    8566           0 :     if (sibling) {
    8567           0 :       if (IsBlockNode(*sibling) || sibling->IsHTMLElement(nsGkAtoms::br)) {
    8568           0 :         foundCR = true;
    8569             :       }
    8570             :     } else {
    8571           0 :       foundCR = true;
    8572             :     }
    8573             :   }
    8574           0 :   if (!foundCR) {
    8575           0 :     int32_t offset = 0;
    8576           0 :     if (!aStarts) {
    8577           0 :       offset = aNode.GetChildCount();
    8578             :     }
    8579           0 :     NS_ENSURE_STATE(mHTMLEditor);
    8580           0 :     RefPtr<Element> brNode = mHTMLEditor->CreateBR(&aNode, offset);
    8581           0 :     if (NS_WARN_IF(!brNode)) {
    8582           0 :       return NS_ERROR_FAILURE;
    8583             :     }
    8584             :   }
    8585           0 :   return NS_OK;
    8586             : }
    8587             : 
    8588             : nsresult
    8589           0 : HTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsINode& aNode)
    8590             : {
    8591           0 :   nsresult rv = MakeSureElemStartsOrEndsOnCR(aNode, false);
    8592           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8593           0 :   return MakeSureElemStartsOrEndsOnCR(aNode, true);
    8594             : }
    8595             : 
    8596             : nsresult
    8597           0 : HTMLEditRules::AlignBlock(Element& aElement,
    8598             :                           const nsAString& aAlignType,
    8599             :                           ContentsOnly aContentsOnly)
    8600             : {
    8601           0 :   if (!IsBlockNode(aElement) && !aElement.IsHTMLElement(nsGkAtoms::hr)) {
    8602             :     // We deal only with blocks; early way out
    8603           0 :     return NS_OK;
    8604             :   }
    8605             : 
    8606           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8607           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    8608             : 
    8609           0 :   nsresult rv = RemoveAlignment(aElement, aAlignType,
    8610           0 :                                 aContentsOnly == ContentsOnly::yes);
    8611           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8612           0 :   if (htmlEditor->IsCSSEnabled()) {
    8613             :     // Let's use CSS alignment; we use margin-left and margin-right for tables
    8614             :     // and text-align for other block-level elements
    8615           0 :     rv = htmlEditor->SetAttributeOrEquivalent(
    8616           0 :                        &aElement, nsGkAtoms::align, aAlignType, false);
    8617           0 :     NS_ENSURE_SUCCESS(rv, rv);
    8618             :   } else {
    8619             :     // HTML case; this code is supposed to be called ONLY if the element
    8620             :     // supports the align attribute but we'll never know...
    8621           0 :     if (HTMLEditUtils::SupportsAlignAttr(aElement)) {
    8622           0 :       rv = htmlEditor->SetAttribute(&aElement, nsGkAtoms::align, aAlignType);
    8623           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8624             :     }
    8625             :   }
    8626           0 :   return NS_OK;
    8627             : }
    8628             : 
    8629             : nsresult
    8630           0 : HTMLEditRules::ChangeIndentation(Element& aElement,
    8631             :                                  Change aChange)
    8632             : {
    8633           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8634           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    8635             : 
    8636             :   nsIAtom& marginProperty =
    8637           0 :     MarginPropertyAtomForIndent(*htmlEditor->mCSSEditUtils, aElement);
    8638           0 :   nsAutoString value;
    8639           0 :   htmlEditor->mCSSEditUtils->GetSpecifiedProperty(aElement, marginProperty,
    8640           0 :                                                   value);
    8641             :   float f;
    8642           0 :   nsCOMPtr<nsIAtom> unit;
    8643           0 :   htmlEditor->mCSSEditUtils->ParseLength(value, &f, getter_AddRefs(unit));
    8644           0 :   if (!f) {
    8645           0 :     nsAutoString defaultLengthUnit;
    8646           0 :     htmlEditor->mCSSEditUtils->GetDefaultLengthUnit(defaultLengthUnit);
    8647           0 :     unit = NS_Atomize(defaultLengthUnit);
    8648             :   }
    8649           0 :   int8_t multiplier = aChange == Change::plus ? +1 : -1;
    8650           0 :   if (nsGkAtoms::in == unit) {
    8651           0 :     f += NS_EDITOR_INDENT_INCREMENT_IN * multiplier;
    8652           0 :   } else if (nsGkAtoms::cm == unit) {
    8653           0 :     f += NS_EDITOR_INDENT_INCREMENT_CM * multiplier;
    8654           0 :   } else if (nsGkAtoms::mm == unit) {
    8655           0 :     f += NS_EDITOR_INDENT_INCREMENT_MM * multiplier;
    8656           0 :   } else if (nsGkAtoms::pt == unit) {
    8657           0 :     f += NS_EDITOR_INDENT_INCREMENT_PT * multiplier;
    8658           0 :   } else if (nsGkAtoms::pc == unit) {
    8659           0 :     f += NS_EDITOR_INDENT_INCREMENT_PC * multiplier;
    8660           0 :   } else if (nsGkAtoms::em == unit) {
    8661           0 :     f += NS_EDITOR_INDENT_INCREMENT_EM * multiplier;
    8662           0 :   } else if (nsGkAtoms::ex == unit) {
    8663           0 :     f += NS_EDITOR_INDENT_INCREMENT_EX * multiplier;
    8664           0 :   } else if (nsGkAtoms::px == unit) {
    8665           0 :     f += NS_EDITOR_INDENT_INCREMENT_PX * multiplier;
    8666           0 :   } else if (nsGkAtoms::percentage == unit) {
    8667           0 :     f += NS_EDITOR_INDENT_INCREMENT_PERCENT * multiplier;
    8668             :   }
    8669             : 
    8670           0 :   if (0 < f) {
    8671           0 :     nsAutoString newValue;
    8672           0 :     newValue.AppendFloat(f);
    8673           0 :     newValue.Append(nsDependentAtomString(unit));
    8674           0 :     htmlEditor->mCSSEditUtils->SetCSSProperty(aElement, marginProperty,
    8675           0 :                                               newValue);
    8676           0 :     return NS_OK;
    8677             :   }
    8678             : 
    8679           0 :   htmlEditor->mCSSEditUtils->RemoveCSSProperty(aElement, marginProperty,
    8680           0 :                                                value);
    8681             : 
    8682             :   // Remove unnecessary divs
    8683           0 :   if (!aElement.IsHTMLElement(nsGkAtoms::div) ||
    8684           0 :       &aElement == htmlEditor->GetActiveEditingHost() ||
    8685           0 :       !htmlEditor->IsDescendantOfEditorRoot(&aElement) ||
    8686           0 :       HTMLEditor::HasAttributes(&aElement)) {
    8687           0 :     return NS_OK;
    8688             :   }
    8689             : 
    8690           0 :   nsresult rv = htmlEditor->RemoveContainer(&aElement);
    8691           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8692             : 
    8693           0 :   return NS_OK;
    8694             : }
    8695             : 
    8696             : nsresult
    8697           0 : HTMLEditRules::WillAbsolutePosition(Selection& aSelection,
    8698             :                                     bool* aCancel,
    8699             :                                     bool* aHandled)
    8700             : {
    8701           0 :   MOZ_ASSERT(aCancel && aHandled);
    8702           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8703           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    8704             : 
    8705           0 :   WillInsert(aSelection, aCancel);
    8706             : 
    8707             :   // We want to ignore result of WillInsert()
    8708           0 :   *aCancel = false;
    8709           0 :   *aHandled = true;
    8710             : 
    8711           0 :   nsCOMPtr<Element> focusElement = htmlEditor->GetSelectionContainer();
    8712           0 :   if (focusElement && HTMLEditUtils::IsImage(focusElement)) {
    8713           0 :     mNewBlock = focusElement;
    8714           0 :     return NS_OK;
    8715             :   }
    8716             : 
    8717           0 :   nsresult rv = NormalizeSelection(&aSelection);
    8718           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8719           0 :   AutoSelectionRestorer selectionRestorer(&aSelection, htmlEditor);
    8720             : 
    8721             :   // Convert the selection ranges into "promoted" selection ranges: this
    8722             :   // basically just expands the range to include the immediate block parent,
    8723             :   // and then further expands to include any ancestors whose children are all
    8724             :   // in the range.
    8725             : 
    8726           0 :   nsTArray<RefPtr<nsRange>> arrayOfRanges;
    8727             :   GetPromotedRanges(aSelection, arrayOfRanges,
    8728           0 :                     EditAction::setAbsolutePosition);
    8729             : 
    8730             :   // Use these ranges to contruct a list of nodes to act on.
    8731           0 :   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
    8732             :   rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
    8733           0 :                             EditAction::setAbsolutePosition);
    8734           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8735             : 
    8736             :   // If nothing visible in list, make an empty block
    8737           0 :   if (ListIsEmptyLine(arrayOfNodes)) {
    8738             :     // Get selection location
    8739           0 :     NS_ENSURE_STATE(aSelection.GetRangeAt(0) &&
    8740             :                     aSelection.GetRangeAt(0)->GetStartContainer());
    8741             :     OwningNonNull<nsINode> parent =
    8742           0 :       *aSelection.GetRangeAt(0)->GetStartContainer();
    8743           0 :     int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
    8744             : 
    8745             :     // Make sure we can put a block here
    8746           0 :     rv = SplitAsNeeded(*nsGkAtoms::div, parent, offset);
    8747           0 :     NS_ENSURE_SUCCESS(rv, rv);
    8748             :     nsCOMPtr<Element> positionedDiv =
    8749           0 :       htmlEditor->CreateNode(nsGkAtoms::div, parent, offset);
    8750           0 :     NS_ENSURE_STATE(positionedDiv);
    8751             :     // Remember our new block for postprocessing
    8752           0 :     mNewBlock = positionedDiv;
    8753             :     // Delete anything that was in the list of nodes
    8754           0 :     while (!arrayOfNodes.IsEmpty()) {
    8755           0 :       OwningNonNull<nsINode> curNode = arrayOfNodes[0];
    8756           0 :       rv = htmlEditor->DeleteNode(curNode);
    8757           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8758           0 :       arrayOfNodes.RemoveElementAt(0);
    8759             :     }
    8760             :     // Put selection in new block
    8761           0 :     *aHandled = true;
    8762           0 :     rv = aSelection.Collapse(positionedDiv, 0);
    8763             :     // Don't restore the selection
    8764           0 :     selectionRestorer.Abort();
    8765           0 :     NS_ENSURE_SUCCESS(rv, rv);
    8766           0 :     return NS_OK;
    8767             :   }
    8768             : 
    8769             :   // Okay, now go through all the nodes and put them in a blockquote, or
    8770             :   // whatever is appropriate.  Woohoo!
    8771           0 :   nsCOMPtr<Element> curList, curPositionedDiv, indentedLI;
    8772           0 :   for (uint32_t i = 0; i < arrayOfNodes.Length(); i++) {
    8773             :     // Here's where we actually figure out what to do
    8774           0 :     NS_ENSURE_STATE(arrayOfNodes[i]->IsContent());
    8775           0 :     OwningNonNull<nsIContent> curNode = *arrayOfNodes[i]->AsContent();
    8776             : 
    8777             :     // Ignore all non-editable nodes.  Leave them be.
    8778           0 :     if (!htmlEditor->IsEditable(curNode)) {
    8779           0 :       continue;
    8780             :     }
    8781             : 
    8782           0 :     nsCOMPtr<nsIContent> sibling;
    8783             : 
    8784           0 :     nsCOMPtr<nsINode> curParent = curNode->GetParentNode();
    8785           0 :     int32_t offset = curParent ? curParent->IndexOf(curNode) : -1;
    8786             : 
    8787             :     // Some logic for putting list items into nested lists...
    8788           0 :     if (HTMLEditUtils::IsList(curParent)) {
    8789             :       // Check to see if curList is still appropriate.  Which it is if curNode
    8790             :       // is still right after it in the same list.
    8791           0 :       if (curList) {
    8792           0 :         sibling = htmlEditor->GetPriorHTMLSibling(curNode);
    8793             :       }
    8794             : 
    8795           0 :       if (!curList || (sibling && sibling != curList)) {
    8796             :         // Create a new nested list of correct type
    8797             :         rv =
    8798           0 :           SplitAsNeeded(*curParent->NodeInfo()->NameAtom(), curParent, offset);
    8799           0 :         NS_ENSURE_SUCCESS(rv, rv);
    8800           0 :         if (!curPositionedDiv) {
    8801           0 :           nsCOMPtr<nsINode> curParentParent = curParent->GetParentNode();
    8802             :           int32_t parentOffset = curParentParent
    8803           0 :             ? curParentParent->IndexOf(curParent) : -1;
    8804           0 :           curPositionedDiv = htmlEditor->CreateNode(nsGkAtoms::div, curParentParent,
    8805           0 :                                                     parentOffset);
    8806           0 :           mNewBlock = curPositionedDiv;
    8807             :         }
    8808           0 :         curList = htmlEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
    8809           0 :                                          curPositionedDiv, -1);
    8810           0 :         NS_ENSURE_STATE(curList);
    8811             :         // curList is now the correct thing to put curNode in.  Remember our
    8812             :         // new block for postprocessing.
    8813             :       }
    8814             :       // Tuck the node into the end of the active list
    8815           0 :       rv = htmlEditor->MoveNode(curNode, curList, -1);
    8816           0 :       NS_ENSURE_SUCCESS(rv, rv);
    8817             :     } else {
    8818             :       // Not a list item, use blockquote?  If we are inside a list item, we
    8819             :       // don't want to blockquote, we want to sublist the list item.  We may
    8820             :       // have several nodes listed in the array of nodes to act on, that are in
    8821             :       // the same list item.  Since we only want to indent that li once, we
    8822             :       // must keep track of the most recent indented list item, and not indent
    8823             :       // it if we find another node to act on that is still inside the same li.
    8824           0 :       nsCOMPtr<Element> listItem = IsInListItem(curNode);
    8825           0 :       if (listItem) {
    8826           0 :         if (indentedLI == listItem) {
    8827             :           // Already indented this list item
    8828           0 :           continue;
    8829             :         }
    8830           0 :         curParent = listItem->GetParentNode();
    8831           0 :         offset = curParent ? curParent->IndexOf(listItem) : -1;
    8832             :         // Check to see if curList is still appropriate.  Which it is if
    8833             :         // curNode is still right after it in the same list.
    8834           0 :         if (curList) {
    8835           0 :           sibling = htmlEditor->GetPriorHTMLSibling(curNode);
    8836             :         }
    8837             : 
    8838           0 :         if (!curList || (sibling && sibling != curList)) {
    8839             :           // Create a new nested list of correct type
    8840           0 :           rv = SplitAsNeeded(*curParent->NodeInfo()->NameAtom(), curParent,
    8841           0 :                              offset);
    8842           0 :           NS_ENSURE_SUCCESS(rv, rv);
    8843           0 :           if (!curPositionedDiv) {
    8844           0 :             nsCOMPtr<nsINode> curParentParent = curParent->GetParentNode();
    8845           0 :             int32_t parentOffset = curParentParent ?
    8846           0 :               curParentParent->IndexOf(curParent) : -1;
    8847           0 :             curPositionedDiv = htmlEditor->CreateNode(nsGkAtoms::div,
    8848             :                                                       curParentParent,
    8849           0 :                                                       parentOffset);
    8850           0 :             mNewBlock = curPositionedDiv;
    8851             :           }
    8852           0 :           curList = htmlEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
    8853           0 :                                            curPositionedDiv, -1);
    8854           0 :           NS_ENSURE_STATE(curList);
    8855             :         }
    8856           0 :         rv = htmlEditor->MoveNode(listItem, curList, -1);
    8857           0 :         NS_ENSURE_SUCCESS(rv, rv);
    8858             :         // Remember we indented this li
    8859           0 :         indentedLI = listItem;
    8860             :       } else {
    8861             :         // Need to make a div to put things in if we haven't already
    8862             : 
    8863           0 :         if (!curPositionedDiv) {
    8864           0 :           if (curNode->IsHTMLElement(nsGkAtoms::div)) {
    8865           0 :             curPositionedDiv = curNode->AsElement();
    8866           0 :             mNewBlock = curPositionedDiv;
    8867           0 :             curList = nullptr;
    8868           0 :             continue;
    8869             :           }
    8870           0 :           rv = SplitAsNeeded(*nsGkAtoms::div, curParent, offset);
    8871           0 :           NS_ENSURE_SUCCESS(rv, rv);
    8872           0 :           curPositionedDiv = htmlEditor->CreateNode(nsGkAtoms::div, curParent,
    8873           0 :                                                     offset);
    8874           0 :           NS_ENSURE_STATE(curPositionedDiv);
    8875             :           // Remember our new block for postprocessing
    8876           0 :           mNewBlock = curPositionedDiv;
    8877             :           // curPositionedDiv is now the correct thing to put curNode in
    8878             :         }
    8879             : 
    8880             :         // Tuck the node into the end of the active blockquote
    8881           0 :         rv = htmlEditor->MoveNode(curNode, curPositionedDiv, -1);
    8882           0 :         NS_ENSURE_SUCCESS(rv, rv);
    8883             :         // Forget curList, if any
    8884           0 :         curList = nullptr;
    8885             :       }
    8886             :     }
    8887             :   }
    8888           0 :   return NS_OK;
    8889             : }
    8890             : 
    8891             : nsresult
    8892           0 : HTMLEditRules::DidAbsolutePosition()
    8893             : {
    8894           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8895           0 :   nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
    8896             :   nsCOMPtr<nsIDOMElement> elt =
    8897           0 :     static_cast<nsIDOMElement*>(GetAsDOMNode(mNewBlock));
    8898           0 :   return absPosHTMLEditor->AbsolutelyPositionElement(elt, true);
    8899             : }
    8900             : 
    8901             : nsresult
    8902           0 : HTMLEditRules::WillRemoveAbsolutePosition(Selection* aSelection,
    8903             :                                           bool* aCancel,
    8904             :                                           bool* aHandled) {
    8905           0 :   if (!aSelection || !aCancel || !aHandled) {
    8906           0 :     return NS_ERROR_NULL_POINTER;
    8907             :   }
    8908           0 :   WillInsert(*aSelection, aCancel);
    8909             : 
    8910             :   // initialize out param
    8911             :   // we want to ignore aCancel from WillInsert()
    8912           0 :   *aCancel = false;
    8913           0 :   *aHandled = true;
    8914             : 
    8915           0 :   nsCOMPtr<nsIDOMElement>  elt;
    8916           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8917             :   nsresult rv =
    8918           0 :     mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt));
    8919           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8920             : 
    8921           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8922           0 :   AutoSelectionRestorer selectionRestorer(aSelection, mHTMLEditor);
    8923             : 
    8924           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8925           0 :   nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
    8926           0 :   return absPosHTMLEditor->AbsolutelyPositionElement(elt, false);
    8927             : }
    8928             : 
    8929             : nsresult
    8930           0 : HTMLEditRules::WillRelativeChangeZIndex(Selection* aSelection,
    8931             :                                         int32_t aChange,
    8932             :                                         bool* aCancel,
    8933             :                                         bool* aHandled)
    8934             : {
    8935           0 :   if (!aSelection || !aCancel || !aHandled) {
    8936           0 :     return NS_ERROR_NULL_POINTER;
    8937             :   }
    8938           0 :   WillInsert(*aSelection, aCancel);
    8939             : 
    8940             :   // initialize out param
    8941             :   // we want to ignore aCancel from WillInsert()
    8942           0 :   *aCancel = false;
    8943           0 :   *aHandled = true;
    8944             : 
    8945           0 :   nsCOMPtr<nsIDOMElement>  elt;
    8946           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8947             :   nsresult rv =
    8948           0 :     mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt));
    8949           0 :   NS_ENSURE_SUCCESS(rv, rv);
    8950             : 
    8951           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8952           0 :   AutoSelectionRestorer selectionRestorer(aSelection, mHTMLEditor);
    8953             : 
    8954           0 :   NS_ENSURE_STATE(mHTMLEditor);
    8955           0 :   nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
    8956             :   int32_t zIndex;
    8957           0 :   return absPosHTMLEditor->RelativeChangeElementZIndex(elt, aChange, &zIndex);
    8958             : }
    8959             : 
    8960             : NS_IMETHODIMP
    8961           0 : HTMLEditRules::DocumentModified()
    8962             : {
    8963           0 :   nsContentUtils::AddScriptRunner(
    8964           0 :     NewRunnableMethod("HTMLEditRules::DocumentModifiedWorker",
    8965             :                       this,
    8966           0 :                       &HTMLEditRules::DocumentModifiedWorker));
    8967           0 :   return NS_OK;
    8968             : }
    8969             : 
    8970             : void
    8971           0 : HTMLEditRules::DocumentModifiedWorker()
    8972             : {
    8973           0 :   if (!mHTMLEditor) {
    8974           0 :     return;
    8975             :   }
    8976             : 
    8977             :   // DeleteNode below may cause a flush, which could destroy the editor
    8978           0 :   nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
    8979             : 
    8980           0 :   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
    8981           0 :   RefPtr<Selection> selection = htmlEditor->GetSelection();
    8982           0 :   if (!selection) {
    8983           0 :     return;
    8984             :   }
    8985             : 
    8986             :   // Delete our bogus node, if we have one, since the document might not be
    8987             :   // empty any more.
    8988           0 :   if (mBogusNode) {
    8989           0 :     htmlEditor->DeleteNode(mBogusNode);
    8990           0 :     mBogusNode = nullptr;
    8991             :   }
    8992             : 
    8993             :   // Try to recreate the bogus node if needed.
    8994           0 :   CreateBogusNodeIfNeeded(selection);
    8995             : }
    8996             : 
    8997             : } // namespace mozilla

Generated by: LCOV version 1.13