LCOV - code coverage report
Current view: top level - editor/libeditor - HTMLEditorDataTransfer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1142 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 49 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=78: */
       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 "mozilla/HTMLEditor.h"
       8             : 
       9             : #include <string.h>
      10             : 
      11             : #include "HTMLEditUtils.h"
      12             : #include "TextEditUtils.h"
      13             : #include "WSRunObject.h"
      14             : #include "mozilla/dom/DataTransfer.h"
      15             : #include "mozilla/dom/DocumentFragment.h"
      16             : #include "mozilla/dom/DOMStringList.h"
      17             : #include "mozilla/dom/Selection.h"
      18             : #include "mozilla/ArrayUtils.h"
      19             : #include "mozilla/Base64.h"
      20             : #include "mozilla/BasicEvents.h"
      21             : #include "mozilla/EditorUtils.h"
      22             : #include "mozilla/OwningNonNull.h"
      23             : #include "mozilla/Preferences.h"
      24             : #include "mozilla/SelectionState.h"
      25             : #include "nsAString.h"
      26             : #include "nsCOMPtr.h"
      27             : #include "nsCRTGlue.h" // for CRLF
      28             : #include "nsComponentManagerUtils.h"
      29             : #include "nsIScriptError.h"
      30             : #include "nsContentUtils.h"
      31             : #include "nsDebug.h"
      32             : #include "nsDependentSubstring.h"
      33             : #include "nsError.h"
      34             : #include "nsGkAtoms.h"
      35             : #include "nsIClipboard.h"
      36             : #include "nsIContent.h"
      37             : #include "nsIContentFilter.h"
      38             : #include "nsIDOMComment.h"
      39             : #include "nsIDOMDocument.h"
      40             : #include "nsIDOMDocumentFragment.h"
      41             : #include "nsIDOMElement.h"
      42             : #include "nsIDOMHTMLAnchorElement.h"
      43             : #include "nsIDOMHTMLEmbedElement.h"
      44             : #include "nsIDOMHTMLFrameElement.h"
      45             : #include "nsIDOMHTMLIFrameElement.h"
      46             : #include "nsIDOMHTMLImageElement.h"
      47             : #include "nsIDOMHTMLInputElement.h"
      48             : #include "nsIDOMHTMLLinkElement.h"
      49             : #include "nsIDOMHTMLObjectElement.h"
      50             : #include "nsIDOMHTMLScriptElement.h"
      51             : #include "nsIDOMNode.h"
      52             : #include "nsIDocument.h"
      53             : #include "nsIEditRules.h"
      54             : #include "nsIFile.h"
      55             : #include "nsIInputStream.h"
      56             : #include "nsIMIMEService.h"
      57             : #include "nsNameSpaceManager.h"
      58             : #include "nsINode.h"
      59             : #include "nsIParserUtils.h"
      60             : #include "nsISupportsImpl.h"
      61             : #include "nsISupportsPrimitives.h"
      62             : #include "nsISupportsUtils.h"
      63             : #include "nsITransferable.h"
      64             : #include "nsIURI.h"
      65             : #include "nsIVariant.h"
      66             : #include "nsLinebreakConverter.h"
      67             : #include "nsLiteralString.h"
      68             : #include "nsNetUtil.h"
      69             : #include "nsRange.h"
      70             : #include "nsReadableUtils.h"
      71             : #include "nsServiceManagerUtils.h"
      72             : #include "nsStreamUtils.h"
      73             : #include "nsString.h"
      74             : #include "nsStringFwd.h"
      75             : #include "nsStringIterator.h"
      76             : #include "nsSubstringTuple.h"
      77             : #include "nsTreeSanitizer.h"
      78             : #include "nsXPCOM.h"
      79             : #include "nscore.h"
      80             : #include "nsContentUtils.h"
      81             : 
      82             : class nsIAtom;
      83             : class nsILoadContext;
      84             : class nsISupports;
      85             : 
      86             : namespace mozilla {
      87             : 
      88             : using namespace dom;
      89             : 
      90             : #define kInsertCookie  "_moz_Insert Here_moz_"
      91             : 
      92             : // some little helpers
      93             : static bool FindIntegerAfterString(const char* aLeadingString,
      94             :                                    nsCString& aCStr, int32_t& foundNumber);
      95             : static nsresult RemoveFragComments(nsCString& theStr);
      96             : static void RemoveBodyAndHead(nsINode& aNode);
      97             : static nsresult FindTargetNode(nsIDOMNode* aStart,
      98             :                                nsCOMPtr<nsIDOMNode>& aResult);
      99             : 
     100             : nsresult
     101           0 : HTMLEditor::LoadHTML(const nsAString& aInputString)
     102             : {
     103           0 :   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
     104             : 
     105             :   // force IME commit; set up rules sniffing and batching
     106           0 :   ForceCompositionEnd();
     107           0 :   AutoEditBatch beginBatching(this);
     108           0 :   AutoRules beginRulesSniffing(this, EditAction::loadHTML, nsIEditor::eNext);
     109             : 
     110             :   // Get selection
     111           0 :   RefPtr<Selection> selection = GetSelection();
     112           0 :   NS_ENSURE_STATE(selection);
     113             : 
     114           0 :   TextRulesInfo ruleInfo(EditAction::loadHTML);
     115             :   bool cancel, handled;
     116             :   // Protect the edit rules object from dying
     117           0 :   nsCOMPtr<nsIEditRules> rules(mRules);
     118           0 :   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
     119           0 :   NS_ENSURE_SUCCESS(rv, rv);
     120           0 :   if (cancel) {
     121           0 :     return NS_OK; // rules canceled the operation
     122             :   }
     123             : 
     124           0 :   if (!handled) {
     125             :     // Delete Selection, but only if it isn't collapsed, see bug #106269
     126           0 :     if (!selection->Collapsed()) {
     127           0 :       rv = DeleteSelection(eNone, eStrip);
     128           0 :       NS_ENSURE_SUCCESS(rv, rv);
     129             :     }
     130             : 
     131             :     // Get the first range in the selection, for context:
     132           0 :     RefPtr<nsRange> range = selection->GetRangeAt(0);
     133           0 :     NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
     134             : 
     135             :     // create fragment for pasted html
     136           0 :     nsCOMPtr<nsIDOMDocumentFragment> docfrag;
     137           0 :     rv = range->CreateContextualFragment(aInputString, getter_AddRefs(docfrag));
     138           0 :     NS_ENSURE_SUCCESS(rv, rv);
     139             :     // put the fragment into the document
     140           0 :     nsCOMPtr<nsIDOMNode> parent;
     141           0 :     rv = range->GetStartContainer(getter_AddRefs(parent));
     142           0 :     NS_ENSURE_SUCCESS(rv, rv);
     143           0 :     NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
     144             :     int32_t childOffset;
     145           0 :     rv = range->GetStartOffset(&childOffset);
     146           0 :     NS_ENSURE_SUCCESS(rv, rv);
     147             : 
     148           0 :     nsCOMPtr<nsIDOMNode> nodeToInsert;
     149           0 :     docfrag->GetFirstChild(getter_AddRefs(nodeToInsert));
     150           0 :     while (nodeToInsert) {
     151           0 :       rv = InsertNode(nodeToInsert, parent, childOffset++);
     152           0 :       NS_ENSURE_SUCCESS(rv, rv);
     153           0 :       docfrag->GetFirstChild(getter_AddRefs(nodeToInsert));
     154             :     }
     155             :   }
     156             : 
     157           0 :   return rules->DidDoAction(selection, &ruleInfo, rv);
     158             : }
     159             : 
     160             : NS_IMETHODIMP
     161           0 : HTMLEditor::InsertHTML(const nsAString& aInString)
     162             : {
     163           0 :   const nsString& empty = EmptyString();
     164             : 
     165           0 :   return InsertHTMLWithContext(aInString, empty, empty, empty,
     166           0 :                                nullptr,  nullptr, 0, true);
     167             : }
     168             : 
     169             : nsresult
     170           0 : HTMLEditor::InsertHTMLWithContext(const nsAString& aInputString,
     171             :                                   const nsAString& aContextStr,
     172             :                                   const nsAString& aInfoStr,
     173             :                                   const nsAString& aFlavor,
     174             :                                   nsIDOMDocument* aSourceDoc,
     175             :                                   nsIDOMNode* aDestNode,
     176             :                                   int32_t aDestOffset,
     177             :                                   bool aDeleteSelection)
     178             : {
     179           0 :   return DoInsertHTMLWithContext(aInputString, aContextStr, aInfoStr,
     180             :       aFlavor, aSourceDoc, aDestNode, aDestOffset, aDeleteSelection,
     181           0 :       /* trusted input */ true, /* clear style */ false);
     182             : }
     183             : 
     184             : nsresult
     185           0 : HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
     186             :                                     const nsAString& aContextStr,
     187             :                                     const nsAString& aInfoStr,
     188             :                                     const nsAString& aFlavor,
     189             :                                     nsIDOMDocument* aSourceDoc,
     190             :                                     nsIDOMNode* aDestNode,
     191             :                                     int32_t aDestOffset,
     192             :                                     bool aDeleteSelection,
     193             :                                     bool aTrustedInput,
     194             :                                     bool aClearStyle)
     195             : {
     196           0 :   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
     197             : 
     198             :   // Prevent the edit rules object from dying
     199           0 :   nsCOMPtr<nsIEditRules> rules(mRules);
     200             : 
     201             :   // force IME commit; set up rules sniffing and batching
     202           0 :   ForceCompositionEnd();
     203           0 :   AutoEditBatch beginBatching(this);
     204           0 :   AutoRules beginRulesSniffing(this, EditAction::htmlPaste, nsIEditor::eNext);
     205             : 
     206             :   // Get selection
     207           0 :   RefPtr<Selection> selection = GetSelection();
     208           0 :   NS_ENSURE_STATE(selection);
     209             : 
     210             :   // create a dom document fragment that represents the structure to paste
     211           0 :   nsCOMPtr<nsIDOMNode> fragmentAsNode, streamStartParent, streamEndParent;
     212           0 :   int32_t streamStartOffset = 0, streamEndOffset = 0;
     213             : 
     214           0 :   nsresult rv = CreateDOMFragmentFromPaste(aInputString, aContextStr, aInfoStr,
     215             :                                            address_of(fragmentAsNode),
     216             :                                            address_of(streamStartParent),
     217             :                                            address_of(streamEndParent),
     218             :                                            &streamStartOffset,
     219             :                                            &streamEndOffset,
     220           0 :                                            aTrustedInput);
     221           0 :   NS_ENSURE_SUCCESS(rv, rv);
     222             : 
     223           0 :   nsCOMPtr<nsIDOMNode> targetNode;
     224           0 :   int32_t targetOffset=0;
     225             : 
     226           0 :   if (!aDestNode) {
     227             :     // if caller didn't provide the destination/target node,
     228             :     // fetch the paste insertion point from our selection
     229           0 :     rv = GetStartNodeAndOffset(selection, getter_AddRefs(targetNode), &targetOffset);
     230           0 :     NS_ENSURE_SUCCESS(rv, rv);
     231           0 :     if (!targetNode || !IsEditable(targetNode)) {
     232           0 :       return NS_ERROR_FAILURE;
     233             :     }
     234             :   } else {
     235           0 :     targetNode = aDestNode;
     236           0 :     targetOffset = aDestOffset;
     237             :   }
     238             : 
     239           0 :   bool doContinue = true;
     240             : 
     241           0 :   rv = DoContentFilterCallback(aFlavor, aSourceDoc, aDeleteSelection,
     242           0 :                                (nsIDOMNode **)address_of(fragmentAsNode),
     243           0 :                                (nsIDOMNode **)address_of(streamStartParent),
     244             :                                &streamStartOffset,
     245           0 :                                (nsIDOMNode **)address_of(streamEndParent),
     246             :                                &streamEndOffset,
     247           0 :                                (nsIDOMNode **)address_of(targetNode),
     248           0 :                                &targetOffset, &doContinue);
     249             : 
     250           0 :   NS_ENSURE_SUCCESS(rv, rv);
     251           0 :   NS_ENSURE_TRUE(doContinue, NS_OK);
     252             : 
     253             :   // if we have a destination / target node, we want to insert there
     254             :   // rather than in place of the selection
     255             :   // ignore aDeleteSelection here if no aDestNode since deletion will
     256             :   // also occur later; this block is intended to cover the various
     257             :   // scenarios where we are dropping in an editor (and may want to delete
     258             :   // the selection before collapsing the selection in the new destination)
     259           0 :   if (aDestNode) {
     260           0 :     if (aDeleteSelection) {
     261             :       // Use an auto tracker so that our drop point is correctly
     262             :       // positioned after the delete.
     263           0 :       AutoTrackDOMPoint tracker(mRangeUpdater, &targetNode, &targetOffset);
     264           0 :       rv = DeleteSelection(eNone, eStrip);
     265           0 :       NS_ENSURE_SUCCESS(rv, rv);
     266             :     }
     267             : 
     268           0 :     rv = selection->Collapse(targetNode, targetOffset);
     269           0 :     NS_ENSURE_SUCCESS(rv, rv);
     270             :   }
     271             : 
     272             :   // we need to recalculate various things based on potentially new offsets
     273             :   // this is work to be completed at a later date (probably by jfrancis)
     274             : 
     275             :   // make a list of what nodes in docFrag we need to move
     276           0 :   nsTArray<OwningNonNull<nsINode>> nodeList;
     277           0 :   nsCOMPtr<nsINode> fragmentAsNodeNode = do_QueryInterface(fragmentAsNode);
     278           0 :   NS_ENSURE_STATE(fragmentAsNodeNode || !fragmentAsNode);
     279             :   nsCOMPtr<nsINode> streamStartParentNode =
     280           0 :     do_QueryInterface(streamStartParent);
     281           0 :   NS_ENSURE_STATE(streamStartParentNode || !streamStartParent);
     282             :   nsCOMPtr<nsINode> streamEndParentNode =
     283           0 :     do_QueryInterface(streamEndParent);
     284           0 :   NS_ENSURE_STATE(streamEndParentNode || !streamEndParent);
     285           0 :   CreateListOfNodesToPaste(*static_cast<DocumentFragment*>(fragmentAsNodeNode.get()),
     286             :                            nodeList,
     287             :                            streamStartParentNode, streamStartOffset,
     288           0 :                            streamEndParentNode, streamEndOffset);
     289             : 
     290           0 :   if (nodeList.IsEmpty()) {
     291             :     // We aren't inserting anything, but if aDeleteSelection is set, we do want
     292             :     // to delete everything.
     293           0 :     if (aDeleteSelection) {
     294           0 :       return DeleteSelection(eNone, eStrip);
     295             :     }
     296           0 :     return NS_OK;
     297             :   }
     298             : 
     299             :   // Are there any table elements in the list?
     300             :   // node and offset for insertion
     301           0 :   nsCOMPtr<nsIDOMNode> parentNode;
     302             :   int32_t offsetOfNewNode;
     303             : 
     304             :   // check for table cell selection mode
     305           0 :   bool cellSelectionMode = false;
     306           0 :   nsCOMPtr<nsIDOMElement> cell;
     307           0 :   rv = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
     308           0 :   if (NS_SUCCEEDED(rv) && cell) {
     309           0 :     cellSelectionMode = true;
     310             :   }
     311             : 
     312           0 :   if (cellSelectionMode) {
     313             :     // do we have table content to paste?  If so, we want to delete
     314             :     // the selected table cells and replace with new table elements;
     315             :     // but if not we want to delete _contents_ of cells and replace
     316             :     // with non-table elements.  Use cellSelectionMode bool to
     317             :     // indicate results.
     318           0 :     if (!HTMLEditUtils::IsTableElement(nodeList[0])) {
     319           0 :       cellSelectionMode = false;
     320             :     }
     321             :   }
     322             : 
     323           0 :   if (!cellSelectionMode) {
     324           0 :     rv = DeleteSelectionAndPrepareToCreateNode();
     325           0 :     NS_ENSURE_SUCCESS(rv, rv);
     326             : 
     327           0 :     if (aClearStyle) {
     328             :       // pasting does not inherit local inline styles
     329           0 :       nsCOMPtr<nsINode> tmpNode = selection->GetAnchorNode();
     330           0 :       int32_t tmpOffset = static_cast<int32_t>(selection->AnchorOffset());
     331           0 :       rv = ClearStyle(address_of(tmpNode), &tmpOffset, nullptr, nullptr);
     332           0 :       NS_ENSURE_SUCCESS(rv, rv);
     333             :     }
     334             :   } else {
     335             :     // Delete whole cells: we will replace with new table content.
     336             : 
     337             :     // Braces for artificial block to scope AutoSelectionRestorer.
     338             :     // Save current selection since DeleteTableCell() perturbs it.
     339             :     {
     340           0 :       AutoSelectionRestorer selectionRestorer(selection, this);
     341           0 :       rv = DeleteTableCell(1);
     342           0 :       NS_ENSURE_SUCCESS(rv, rv);
     343             :     }
     344             :     // collapse selection to beginning of deleted table content
     345           0 :     selection->CollapseToStart();
     346             :   }
     347             : 
     348             :   // give rules a chance to handle or cancel
     349           0 :   TextRulesInfo ruleInfo(EditAction::insertElement);
     350             :   bool cancel, handled;
     351           0 :   rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
     352           0 :   NS_ENSURE_SUCCESS(rv, rv);
     353           0 :   if (cancel) {
     354           0 :     return NS_OK; // rules canceled the operation
     355             :   }
     356             : 
     357           0 :   if (!handled) {
     358             :     // The rules code (WillDoAction above) might have changed the selection.
     359             :     // refresh our memory...
     360           0 :     rv = GetStartNodeAndOffset(selection, getter_AddRefs(parentNode), &offsetOfNewNode);
     361           0 :     NS_ENSURE_SUCCESS(rv, rv);
     362           0 :     NS_ENSURE_TRUE(parentNode, NS_ERROR_FAILURE);
     363             : 
     364             :     // Adjust position based on the first node we are going to insert.
     365           0 :     NormalizeEOLInsertPosition(nodeList[0], address_of(parentNode),
     366           0 :                                &offsetOfNewNode);
     367             : 
     368             :     // if there are any invisible br's after our insertion point, remove them.
     369             :     // this is because if there is a br at end of what we paste, it will make
     370             :     // the invisible br visible.
     371           0 :     WSRunObject wsObj(this, parentNode, offsetOfNewNode);
     372           0 :     if (wsObj.mEndReasonNode &&
     373           0 :         TextEditUtils::IsBreak(wsObj.mEndReasonNode) &&
     374           0 :         !IsVisBreak(wsObj.mEndReasonNode)) {
     375           0 :       rv = DeleteNode(wsObj.mEndReasonNode);
     376           0 :       NS_ENSURE_SUCCESS(rv, rv);
     377             :     }
     378             : 
     379             :     // Remember if we are in a link.
     380           0 :     bool bStartedInLink = IsInLink(parentNode);
     381             : 
     382             :     // Are we in a text node? If so, split it.
     383           0 :     if (IsTextNode(parentNode)) {
     384           0 :       nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parentNode);
     385           0 :       NS_ENSURE_STATE(parentContent || !parentNode);
     386           0 :       offsetOfNewNode = SplitNodeDeep(*parentContent, *parentContent,
     387             :                                       offsetOfNewNode);
     388           0 :       NS_ENSURE_STATE(offsetOfNewNode != -1);
     389           0 :       nsCOMPtr<nsIDOMNode> temp;
     390           0 :       rv = parentNode->GetParentNode(getter_AddRefs(temp));
     391           0 :       NS_ENSURE_SUCCESS(rv, rv);
     392           0 :       parentNode = temp;
     393             :     }
     394             : 
     395             :     // build up list of parents of first node in list that are either
     396             :     // lists or tables.  First examine front of paste node list.
     397           0 :     nsTArray<OwningNonNull<Element>> startListAndTableArray;
     398             :     GetListAndTableParents(StartOrEnd::start, nodeList,
     399           0 :                            startListAndTableArray);
     400             : 
     401             :     // remember number of lists and tables above us
     402           0 :     int32_t highWaterMark = -1;
     403           0 :     if (!startListAndTableArray.IsEmpty()) {
     404             :       highWaterMark = DiscoverPartialListsAndTables(nodeList,
     405           0 :                                                     startListAndTableArray);
     406             :     }
     407             : 
     408             :     // if we have pieces of tables or lists to be inserted, let's force the paste
     409             :     // to deal with table elements right away, so that it doesn't orphan some
     410             :     // table or list contents outside the table or list.
     411           0 :     if (highWaterMark >= 0) {
     412             :       ReplaceOrphanedStructure(StartOrEnd::start, nodeList,
     413           0 :                                startListAndTableArray, highWaterMark);
     414             :     }
     415             : 
     416             :     // Now go through the same process again for the end of the paste node list.
     417           0 :     nsTArray<OwningNonNull<Element>> endListAndTableArray;
     418           0 :     GetListAndTableParents(StartOrEnd::end, nodeList, endListAndTableArray);
     419           0 :     highWaterMark = -1;
     420             : 
     421             :     // remember number of lists and tables above us
     422           0 :     if (!endListAndTableArray.IsEmpty()) {
     423             :       highWaterMark = DiscoverPartialListsAndTables(nodeList,
     424           0 :                                                     endListAndTableArray);
     425             :     }
     426             : 
     427             :     // don't orphan partial list or table structure
     428           0 :     if (highWaterMark >= 0) {
     429             :       ReplaceOrphanedStructure(StartOrEnd::end, nodeList,
     430           0 :                                endListAndTableArray, highWaterMark);
     431             :     }
     432             : 
     433             :     // Loop over the node list and paste the nodes:
     434           0 :     nsCOMPtr<nsIDOMNode> parentBlock, lastInsertNode, insertedContextParent;
     435           0 :     nsCOMPtr<nsINode> parentNodeNode = do_QueryInterface(parentNode);
     436           0 :     NS_ENSURE_STATE(parentNodeNode || !parentNode);
     437           0 :     if (IsBlockNode(parentNodeNode)) {
     438           0 :       parentBlock = parentNode;
     439             :     } else {
     440           0 :       parentBlock = GetBlockNodeParent(parentNode);
     441             :     }
     442             : 
     443           0 :     int32_t listCount = nodeList.Length();
     444           0 :     for (int32_t j = 0; j < listCount; j++) {
     445           0 :       bool bDidInsert = false;
     446           0 :       nsCOMPtr<nsIDOMNode> curNode = nodeList[j]->AsDOMNode();
     447             : 
     448           0 :       NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE);
     449           0 :       NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE);
     450           0 :       NS_ENSURE_TRUE(!TextEditUtils::IsBody(curNode), NS_ERROR_FAILURE);
     451             : 
     452           0 :       if (insertedContextParent) {
     453             :         // if we had to insert something higher up in the paste hierarchy, we want to
     454             :         // skip any further paste nodes that descend from that.  Else we will paste twice.
     455           0 :         if (EditorUtils::IsDescendantOf(curNode, insertedContextParent)) {
     456           0 :           continue;
     457             :         }
     458             :       }
     459             : 
     460             :       // give the user a hand on table element insertion.  if they have
     461             :       // a table or table row on the clipboard, and are trying to insert
     462             :       // into a table or table row, insert the appropriate children instead.
     463           0 :       if (HTMLEditUtils::IsTableRow(curNode) &&
     464           0 :           HTMLEditUtils::IsTableRow(parentNode) &&
     465           0 :           (HTMLEditUtils::IsTable(curNode) ||
     466           0 :            HTMLEditUtils::IsTable(parentNode))) {
     467           0 :         nsCOMPtr<nsIDOMNode> child;
     468           0 :         curNode->GetFirstChild(getter_AddRefs(child));
     469           0 :         while (child) {
     470           0 :           rv = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, true);
     471           0 :           if (NS_FAILED(rv)) {
     472           0 :             break;
     473             :           }
     474             : 
     475           0 :           bDidInsert = true;
     476           0 :           lastInsertNode = child;
     477           0 :           offsetOfNewNode++;
     478             : 
     479           0 :           curNode->GetFirstChild(getter_AddRefs(child));
     480             :         }
     481             :       }
     482             :       // give the user a hand on list insertion.  if they have
     483             :       // a list on the clipboard, and are trying to insert
     484             :       // into a list or list item, insert the appropriate children instead,
     485             :       // ie, merge the lists instead of pasting in a sublist.
     486           0 :       else if (HTMLEditUtils::IsList(curNode) &&
     487           0 :                (HTMLEditUtils::IsList(parentNode) ||
     488           0 :                 HTMLEditUtils::IsListItem(parentNode))) {
     489           0 :         nsCOMPtr<nsIDOMNode> child, tmp;
     490           0 :         curNode->GetFirstChild(getter_AddRefs(child));
     491           0 :         while (child) {
     492           0 :           if (HTMLEditUtils::IsListItem(child) ||
     493           0 :               HTMLEditUtils::IsList(child)) {
     494             :             // Check if we are pasting into empty list item. If so
     495             :             // delete it and paste into parent list instead.
     496           0 :             if (HTMLEditUtils::IsListItem(parentNode)) {
     497             :               bool isEmpty;
     498           0 :               rv = IsEmptyNode(parentNode, &isEmpty, true);
     499           0 :               if (NS_SUCCEEDED(rv) && isEmpty) {
     500             :                 int32_t newOffset;
     501           0 :                 nsCOMPtr<nsIDOMNode> listNode = GetNodeLocation(parentNode, &newOffset);
     502           0 :                 if (listNode) {
     503           0 :                   DeleteNode(parentNode);
     504           0 :                   parentNode = listNode;
     505           0 :                   offsetOfNewNode = newOffset;
     506             :                 }
     507             :               }
     508             :             }
     509           0 :             rv = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, true);
     510           0 :             if (NS_FAILED(rv)) {
     511           0 :               break;
     512             :             }
     513             : 
     514           0 :             bDidInsert = true;
     515           0 :             lastInsertNode = child;
     516           0 :             offsetOfNewNode++;
     517             :           } else {
     518           0 :             curNode->RemoveChild(child, getter_AddRefs(tmp));
     519             :           }
     520           0 :           curNode->GetFirstChild(getter_AddRefs(child));
     521             :         }
     522           0 :       } else if (parentBlock && HTMLEditUtils::IsPre(parentBlock) &&
     523           0 :                  HTMLEditUtils::IsPre(curNode)) {
     524             :         // Check for pre's going into pre's.
     525           0 :         nsCOMPtr<nsIDOMNode> child;
     526           0 :         curNode->GetFirstChild(getter_AddRefs(child));
     527           0 :         while (child) {
     528           0 :           rv = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, true);
     529           0 :           if (NS_FAILED(rv)) {
     530           0 :             break;
     531             :           }
     532             : 
     533           0 :           bDidInsert = true;
     534           0 :           lastInsertNode = child;
     535           0 :           offsetOfNewNode++;
     536             : 
     537           0 :           curNode->GetFirstChild(getter_AddRefs(child));
     538             :         }
     539             :       }
     540             : 
     541           0 :       if (!bDidInsert || NS_FAILED(rv)) {
     542             :         // try to insert
     543           0 :         rv = InsertNodeAtPoint(curNode, address_of(parentNode), &offsetOfNewNode, true);
     544           0 :         if (NS_SUCCEEDED(rv)) {
     545           0 :           bDidInsert = true;
     546           0 :           lastInsertNode = curNode;
     547             :         }
     548             : 
     549             :         // Assume failure means no legal parent in the document hierarchy,
     550             :         // try again with the parent of curNode in the paste hierarchy.
     551           0 :         nsCOMPtr<nsIDOMNode> parent;
     552           0 :         while (NS_FAILED(rv) && curNode) {
     553           0 :           curNode->GetParentNode(getter_AddRefs(parent));
     554           0 :           if (parent && !TextEditUtils::IsBody(parent)) {
     555           0 :             rv = InsertNodeAtPoint(parent, address_of(parentNode), &offsetOfNewNode, true);
     556           0 :             if (NS_SUCCEEDED(rv)) {
     557           0 :               bDidInsert = true;
     558           0 :               insertedContextParent = parent;
     559           0 :               lastInsertNode = GetChildAt(parentNode, offsetOfNewNode);
     560             :             }
     561             :           }
     562           0 :           curNode = parent;
     563             :         }
     564             :       }
     565           0 :       if (lastInsertNode) {
     566           0 :         parentNode = GetNodeLocation(lastInsertNode, &offsetOfNewNode);
     567           0 :         offsetOfNewNode++;
     568             :       }
     569             :     }
     570             : 
     571             :     // Now collapse the selection to the end of what we just inserted:
     572           0 :     if (lastInsertNode) {
     573             :       // set selection to the end of what we just pasted.
     574           0 :       nsCOMPtr<nsIDOMNode> selNode, tmp, highTable;
     575             :       int32_t selOffset;
     576             : 
     577             :       // but don't cross tables
     578           0 :       if (!HTMLEditUtils::IsTable(lastInsertNode)) {
     579           0 :         nsCOMPtr<nsINode> lastInsertNode_ = do_QueryInterface(lastInsertNode);
     580           0 :         NS_ENSURE_STATE(lastInsertNode_ || !lastInsertNode);
     581           0 :         selNode = GetAsDOMNode(GetLastEditableLeaf(*lastInsertNode_));
     582           0 :         tmp = selNode;
     583           0 :         while (tmp && tmp != lastInsertNode) {
     584           0 :           if (HTMLEditUtils::IsTable(tmp)) {
     585           0 :             highTable = tmp;
     586             :           }
     587           0 :           nsCOMPtr<nsIDOMNode> parent = tmp;
     588           0 :           tmp->GetParentNode(getter_AddRefs(parent));
     589           0 :           tmp = parent;
     590             :         }
     591           0 :         if (highTable) {
     592           0 :           selNode = highTable;
     593             :         }
     594             :       }
     595           0 :       if (!selNode) {
     596           0 :         selNode = lastInsertNode;
     597             :       }
     598           0 :       if (IsTextNode(selNode) ||
     599           0 :           (IsContainer(selNode) && !HTMLEditUtils::IsTable(selNode))) {
     600           0 :         rv = GetLengthOfDOMNode(selNode, (uint32_t&)selOffset);
     601           0 :         NS_ENSURE_SUCCESS(rv, rv);
     602             :       } else {
     603             :         // We need to find a container for selection.  Look up.
     604           0 :         tmp = selNode;
     605           0 :         selNode = GetNodeLocation(tmp, &selOffset);
     606             :         // selNode might be null in case a mutation listener removed
     607             :         // the stuff we just inserted from the DOM.
     608           0 :         NS_ENSURE_STATE(selNode);
     609           0 :         ++selOffset;  // want to be *after* last leaf node in paste
     610             :       }
     611             : 
     612             :       // make sure we don't end up with selection collapsed after an invisible break node
     613           0 :       WSRunObject wsRunObj(this, selNode, selOffset);
     614           0 :       nsCOMPtr<nsINode> visNode;
     615           0 :       int32_t outVisOffset=0;
     616           0 :       WSType visType;
     617           0 :       nsCOMPtr<nsINode> selNode_(do_QueryInterface(selNode));
     618           0 :       wsRunObj.PriorVisibleNode(selNode_, selOffset, address_of(visNode),
     619           0 :                                 &outVisOffset, &visType);
     620           0 :       if (visType == WSType::br) {
     621             :         // we are after a break.  Is it visible?  Despite the name,
     622             :         // PriorVisibleNode does not make that determination for breaks.
     623             :         // It also may not return the break in visNode.  We have to pull it
     624             :         // out of the WSRunObject's state.
     625           0 :         if (!IsVisBreak(wsRunObj.mStartReasonNode)) {
     626             :           // don't leave selection past an invisible break;
     627             :           // reset {selNode,selOffset} to point before break
     628           0 :           selNode = GetNodeLocation(GetAsDOMNode(wsRunObj.mStartReasonNode), &selOffset);
     629             :           // we want to be inside any inline style prior to break
     630           0 :           WSRunObject wsRunObj(this, selNode, selOffset);
     631           0 :           selNode_ = do_QueryInterface(selNode);
     632           0 :           wsRunObj.PriorVisibleNode(selNode_, selOffset, address_of(visNode),
     633           0 :                                     &outVisOffset, &visType);
     634           0 :           if (visType == WSType::text || visType == WSType::normalWS) {
     635           0 :             selNode = GetAsDOMNode(visNode);
     636           0 :             selOffset = outVisOffset;  // PriorVisibleNode already set offset to _after_ the text or ws
     637           0 :           } else if (visType == WSType::special) {
     638             :             // prior visible thing is an image or some other non-text thingy.
     639             :             // We want to be right after it.
     640           0 :             selNode = GetNodeLocation(GetAsDOMNode(wsRunObj.mStartReasonNode), &selOffset);
     641           0 :             ++selOffset;
     642             :           }
     643             :         }
     644             :       }
     645           0 :       selection->Collapse(selNode, selOffset);
     646             : 
     647             :       // if we just pasted a link, discontinue link style
     648           0 :       nsCOMPtr<nsIDOMNode> link;
     649           0 :       if (!bStartedInLink && IsInLink(selNode, address_of(link))) {
     650             :         // so, if we just pasted a link, I split it.  Why do that instead of just
     651             :         // nudging selection point beyond it?  Because it might have ended in a BR
     652             :         // that is not visible.  If so, the code above just placed selection
     653             :         // inside that.  So I split it instead.
     654           0 :         nsCOMPtr<nsIContent> linkContent = do_QueryInterface(link);
     655           0 :         NS_ENSURE_STATE(linkContent || !link);
     656           0 :         nsCOMPtr<nsIContent> selContent = do_QueryInterface(selNode);
     657           0 :         NS_ENSURE_STATE(selContent || !selNode);
     658           0 :         nsCOMPtr<nsIContent> leftLink;
     659           0 :         SplitNodeDeep(*linkContent, *selContent, selOffset,
     660           0 :                       EmptyContainers::no, getter_AddRefs(leftLink));
     661           0 :         if (leftLink) {
     662           0 :           selNode = GetNodeLocation(GetAsDOMNode(leftLink), &selOffset);
     663           0 :           selection->Collapse(selNode, selOffset+1);
     664             :         }
     665             :       }
     666             :     }
     667             :   }
     668             : 
     669           0 :   return rules->DidDoAction(selection, &ruleInfo, rv);
     670             : }
     671             : 
     672             : NS_IMETHODIMP
     673           0 : HTMLEditor::AddInsertionListener(nsIContentFilter* aListener)
     674             : {
     675           0 :   NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
     676             : 
     677             :   // don't let a listener be added more than once
     678           0 :   if (!mContentFilters.Contains(aListener)) {
     679           0 :     mContentFilters.AppendElement(*aListener);
     680             :   }
     681             : 
     682           0 :   return NS_OK;
     683             : }
     684             : 
     685             : NS_IMETHODIMP
     686           0 : HTMLEditor::RemoveInsertionListener(nsIContentFilter* aListener)
     687             : {
     688           0 :   NS_ENSURE_TRUE(aListener, NS_ERROR_FAILURE);
     689             : 
     690           0 :   mContentFilters.RemoveElement(aListener);
     691             : 
     692           0 :   return NS_OK;
     693             : }
     694             : 
     695             : nsresult
     696           0 : HTMLEditor::DoContentFilterCallback(const nsAString& aFlavor,
     697             :                                     nsIDOMDocument* sourceDoc,
     698             :                                     bool aWillDeleteSelection,
     699             :                                     nsIDOMNode** aFragmentAsNode,
     700             :                                     nsIDOMNode** aFragStartNode,
     701             :                                     int32_t* aFragStartOffset,
     702             :                                     nsIDOMNode** aFragEndNode,
     703             :                                     int32_t* aFragEndOffset,
     704             :                                     nsIDOMNode** aTargetNode,
     705             :                                     int32_t* aTargetOffset,
     706             :                                     bool* aDoContinue)
     707             : {
     708           0 :   *aDoContinue = true;
     709             : 
     710           0 :   for (auto& listener : mContentFilters) {
     711           0 :     if (!*aDoContinue) {
     712           0 :       break;
     713             :     }
     714           0 :     listener->NotifyOfInsertion(aFlavor, nullptr, sourceDoc,
     715             :                                 aWillDeleteSelection, aFragmentAsNode,
     716             :                                 aFragStartNode, aFragStartOffset,
     717             :                                 aFragEndNode, aFragEndOffset, aTargetNode,
     718           0 :                                 aTargetOffset, aDoContinue);
     719             :   }
     720             : 
     721           0 :   return NS_OK;
     722             : }
     723             : 
     724             : bool
     725           0 : HTMLEditor::IsInLink(nsIDOMNode* aNode,
     726             :                      nsCOMPtr<nsIDOMNode>* outLink)
     727             : {
     728           0 :   NS_ENSURE_TRUE(aNode, false);
     729           0 :   if (outLink) {
     730           0 :     *outLink = nullptr;
     731             :   }
     732           0 :   nsCOMPtr<nsIDOMNode> tmp, node = aNode;
     733           0 :   while (node) {
     734           0 :     if (HTMLEditUtils::IsLink(node)) {
     735           0 :       if (outLink) {
     736           0 :         *outLink = node;
     737             :       }
     738           0 :       return true;
     739             :     }
     740           0 :     tmp = node;
     741           0 :     tmp->GetParentNode(getter_AddRefs(node));
     742             :   }
     743           0 :   return false;
     744             : }
     745             : 
     746             : nsresult
     747           0 : HTMLEditor::StripFormattingNodes(nsIContent& aNode,
     748             :                                  bool aListOnly)
     749             : {
     750           0 :   if (aNode.TextIsOnlyWhitespace()) {
     751           0 :     nsCOMPtr<nsINode> parent = aNode.GetParentNode();
     752           0 :     if (parent) {
     753           0 :       if (!aListOnly || HTMLEditUtils::IsList(parent)) {
     754           0 :         ErrorResult rv;
     755           0 :         parent->RemoveChild(aNode, rv);
     756           0 :         return rv.StealNSResult();
     757             :       }
     758           0 :       return NS_OK;
     759             :     }
     760             :   }
     761             : 
     762           0 :   if (!aNode.IsHTMLElement(nsGkAtoms::pre)) {
     763           0 :     nsCOMPtr<nsIContent> child = aNode.GetLastChild();
     764           0 :     while (child) {
     765           0 :       nsCOMPtr<nsIContent> previous = child->GetPreviousSibling();
     766           0 :       nsresult rv = StripFormattingNodes(*child, aListOnly);
     767           0 :       NS_ENSURE_SUCCESS(rv, rv);
     768           0 :       child = previous.forget();
     769             :     }
     770             :   }
     771           0 :   return NS_OK;
     772             : }
     773             : 
     774             : NS_IMETHODIMP
     775           0 : HTMLEditor::PrepareTransferable(nsITransferable** aTransferable)
     776             : {
     777           0 :   return NS_OK;
     778             : }
     779             : 
     780             : nsresult
     781           0 : HTMLEditor::PrepareHTMLTransferable(nsITransferable** aTransferable)
     782             : {
     783             :   // Create generic Transferable for getting the data
     784           0 :   nsresult rv = CallCreateInstance("@mozilla.org/widget/transferable;1", aTransferable);
     785           0 :   NS_ENSURE_SUCCESS(rv, rv);
     786             : 
     787             :   // Get the nsITransferable interface for getting the data from the clipboard
     788           0 :   if (aTransferable) {
     789           0 :     nsCOMPtr<nsIDocument> destdoc = GetDocument();
     790           0 :     nsILoadContext* loadContext = destdoc ? destdoc->GetLoadContext() : nullptr;
     791           0 :     (*aTransferable)->Init(loadContext);
     792             : 
     793             :     // Create the desired DataFlavor for the type of data
     794             :     // we want to get out of the transferable
     795             :     // This should only happen in html editors, not plaintext
     796           0 :     if (!IsPlaintextEditor()) {
     797           0 :       (*aTransferable)->AddDataFlavor(kNativeHTMLMime);
     798           0 :       (*aTransferable)->AddDataFlavor(kHTMLMime);
     799           0 :       (*aTransferable)->AddDataFlavor(kFileMime);
     800             : 
     801           0 :       switch (Preferences::GetInt("clipboard.paste_image_type", 1)) {
     802             :         case 0:  // prefer JPEG over PNG over GIF encoding
     803           0 :           (*aTransferable)->AddDataFlavor(kJPEGImageMime);
     804           0 :           (*aTransferable)->AddDataFlavor(kJPGImageMime);
     805           0 :           (*aTransferable)->AddDataFlavor(kPNGImageMime);
     806           0 :           (*aTransferable)->AddDataFlavor(kGIFImageMime);
     807           0 :           break;
     808             :         case 1:  // prefer PNG over JPEG over GIF encoding (default)
     809             :         default:
     810           0 :           (*aTransferable)->AddDataFlavor(kPNGImageMime);
     811           0 :           (*aTransferable)->AddDataFlavor(kJPEGImageMime);
     812           0 :           (*aTransferable)->AddDataFlavor(kJPGImageMime);
     813           0 :           (*aTransferable)->AddDataFlavor(kGIFImageMime);
     814           0 :           break;
     815             :         case 2:  // prefer GIF over JPEG over PNG encoding
     816           0 :           (*aTransferable)->AddDataFlavor(kGIFImageMime);
     817           0 :           (*aTransferable)->AddDataFlavor(kJPEGImageMime);
     818           0 :           (*aTransferable)->AddDataFlavor(kJPGImageMime);
     819           0 :           (*aTransferable)->AddDataFlavor(kPNGImageMime);
     820           0 :           break;
     821             :       }
     822             :     }
     823           0 :     (*aTransferable)->AddDataFlavor(kUnicodeMime);
     824           0 :     (*aTransferable)->AddDataFlavor(kMozTextInternal);
     825             :   }
     826             : 
     827           0 :   return NS_OK;
     828             : }
     829             : 
     830             : bool
     831           0 : FindIntegerAfterString(const char* aLeadingString,
     832             :                        nsCString& aCStr,
     833             :                        int32_t& foundNumber)
     834             : {
     835             :   // first obtain offsets from cfhtml str
     836           0 :   int32_t numFront = aCStr.Find(aLeadingString);
     837           0 :   if (numFront == -1) {
     838           0 :     return false;
     839             :   }
     840           0 :   numFront += strlen(aLeadingString);
     841             : 
     842           0 :   int32_t numBack = aCStr.FindCharInSet(CRLF, numFront);
     843           0 :   if (numBack == -1) {
     844           0 :     return false;
     845             :   }
     846             : 
     847           0 :   nsAutoCString numStr(Substring(aCStr, numFront, numBack-numFront));
     848             :   nsresult errorCode;
     849           0 :   foundNumber = numStr.ToInteger(&errorCode);
     850           0 :   return true;
     851             : }
     852             : 
     853             : nsresult
     854           0 : RemoveFragComments(nsCString& aStr)
     855             : {
     856             :   // remove the StartFragment/EndFragment comments from the str, if present
     857           0 :   int32_t startCommentIndx = aStr.Find("<!--StartFragment");
     858           0 :   if (startCommentIndx >= 0) {
     859           0 :     int32_t startCommentEnd = aStr.Find("-->", false, startCommentIndx);
     860           0 :     if (startCommentEnd > startCommentIndx) {
     861           0 :       aStr.Cut(startCommentIndx, (startCommentEnd + 3) - startCommentIndx);
     862             :     }
     863             :   }
     864           0 :   int32_t endCommentIndx = aStr.Find("<!--EndFragment");
     865           0 :   if (endCommentIndx >= 0) {
     866           0 :     int32_t endCommentEnd = aStr.Find("-->", false, endCommentIndx);
     867           0 :     if (endCommentEnd > endCommentIndx) {
     868           0 :       aStr.Cut(endCommentIndx, (endCommentEnd + 3) - endCommentIndx);
     869             :     }
     870             :   }
     871           0 :   return NS_OK;
     872             : }
     873             : 
     874             : nsresult
     875           0 : HTMLEditor::ParseCFHTML(nsCString& aCfhtml,
     876             :                         char16_t** aStuffToPaste,
     877             :                         char16_t** aCfcontext)
     878             : {
     879             :   // First obtain offsets from cfhtml str.
     880             :   int32_t startHTML, endHTML, startFragment, endFragment;
     881           0 :   if (!FindIntegerAfterString("StartHTML:", aCfhtml, startHTML) ||
     882           0 :       startHTML < -1) {
     883           0 :     return NS_ERROR_FAILURE;
     884             :   }
     885           0 :   if (!FindIntegerAfterString("EndHTML:", aCfhtml, endHTML) ||
     886           0 :       endHTML < -1) {
     887           0 :     return NS_ERROR_FAILURE;
     888             :   }
     889           0 :   if (!FindIntegerAfterString("StartFragment:", aCfhtml, startFragment) ||
     890           0 :       startFragment < 0) {
     891           0 :     return NS_ERROR_FAILURE;
     892             :   }
     893           0 :   if (!FindIntegerAfterString("EndFragment:", aCfhtml, endFragment) ||
     894           0 :       startFragment < 0) {
     895           0 :     return NS_ERROR_FAILURE;
     896             :   }
     897             : 
     898             :   // The StartHTML and EndHTML markers are allowed to be -1 to include everything.
     899             :   //   See Reference: MSDN doc entitled "HTML Clipboard Format"
     900             :   //   http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx#unknown_854
     901           0 :   if (startHTML == -1) {
     902           0 :     startHTML = aCfhtml.Find("<!--StartFragment-->");
     903           0 :     if (startHTML == -1) {
     904           0 :       return NS_OK;
     905             :     }
     906             :   }
     907           0 :   if (endHTML == -1) {
     908           0 :     const char endFragmentMarker[] = "<!--EndFragment-->";
     909           0 :     endHTML = aCfhtml.Find(endFragmentMarker);
     910           0 :     if (endHTML == -1) {
     911           0 :       return NS_OK;
     912             :     }
     913           0 :     endHTML += ArrayLength(endFragmentMarker) - 1;
     914             :   }
     915             : 
     916             :   // create context string
     917           0 :   nsAutoCString contextUTF8(Substring(aCfhtml, startHTML, startFragment - startHTML) +
     918           0 :                             NS_LITERAL_CSTRING("<!--" kInsertCookie "-->") +
     919           0 :                             Substring(aCfhtml, endFragment, endHTML - endFragment));
     920             : 
     921             :   // validate startFragment
     922             :   // make sure it's not in the middle of a HTML tag
     923             :   // see bug #228879 for more details
     924           0 :   int32_t curPos = startFragment;
     925           0 :   while (curPos > startHTML) {
     926           0 :     if (aCfhtml[curPos] == '>') {
     927             :       // working backwards, the first thing we see is the end of a tag
     928             :       // so StartFragment is good, so do nothing.
     929           0 :       break;
     930             :     }
     931           0 :     if (aCfhtml[curPos] == '<') {
     932             :       // if we are at the start, then we want to see the '<'
     933           0 :       if (curPos != startFragment) {
     934             :         // working backwards, the first thing we see is the start of a tag
     935             :         // so StartFragment is bad, so we need to update it.
     936           0 :         NS_ERROR("StartFragment byte count in the clipboard looks bad, see bug #228879");
     937           0 :         startFragment = curPos - 1;
     938             :       }
     939           0 :       break;
     940             :     }
     941           0 :     curPos--;
     942             :   }
     943             : 
     944             :   // create fragment string
     945           0 :   nsAutoCString fragmentUTF8(Substring(aCfhtml, startFragment, endFragment-startFragment));
     946             : 
     947             :   // remove the StartFragment/EndFragment comments from the fragment, if present
     948           0 :   RemoveFragComments(fragmentUTF8);
     949             : 
     950             :   // remove the StartFragment/EndFragment comments from the context, if present
     951           0 :   RemoveFragComments(contextUTF8);
     952             : 
     953             :   // convert both strings to usc2
     954           0 :   const nsString& fragUcs2Str = NS_ConvertUTF8toUTF16(fragmentUTF8);
     955           0 :   const nsString& cntxtUcs2Str = NS_ConvertUTF8toUTF16(contextUTF8);
     956             : 
     957             :   // translate platform linebreaks for fragment
     958           0 :   int32_t oldLengthInChars = fragUcs2Str.Length() + 1;  // +1 to include null terminator
     959           0 :   int32_t newLengthInChars = 0;
     960           0 :   *aStuffToPaste = nsLinebreakConverter::ConvertUnicharLineBreaks(fragUcs2Str.get(),
     961             :                                                            nsLinebreakConverter::eLinebreakAny,
     962             :                                                            nsLinebreakConverter::eLinebreakContent,
     963             :                                                            oldLengthInChars, &newLengthInChars);
     964           0 :   NS_ENSURE_TRUE(*aStuffToPaste, NS_ERROR_FAILURE);
     965             : 
     966             :   // translate platform linebreaks for context
     967           0 :   oldLengthInChars = cntxtUcs2Str.Length() + 1;  // +1 to include null terminator
     968           0 :   newLengthInChars = 0;
     969           0 :   *aCfcontext = nsLinebreakConverter::ConvertUnicharLineBreaks(cntxtUcs2Str.get(),
     970             :                                                            nsLinebreakConverter::eLinebreakAny,
     971             :                                                            nsLinebreakConverter::eLinebreakContent,
     972             :                                                            oldLengthInChars, &newLengthInChars);
     973             :   // it's ok for context to be empty.  frag might be whole doc and contain all its context.
     974             : 
     975             :   // we're done!
     976           0 :   return NS_OK;
     977             : }
     978             : 
     979             : static nsresult
     980           0 : ImgFromData(const nsACString& aType, const nsACString& aData, nsString& aOutput)
     981             : {
     982           0 :   nsAutoCString data64;
     983           0 :   nsresult rv = Base64Encode(aData, data64);
     984           0 :   NS_ENSURE_SUCCESS(rv, rv);
     985             : 
     986           0 :   aOutput.AssignLiteral("<IMG src=\"data:");
     987           0 :   AppendUTF8toUTF16(aType, aOutput);
     988           0 :   aOutput.AppendLiteral(";base64,");
     989           0 :   if (!AppendASCIItoUTF16(data64, aOutput, fallible_t())) {
     990           0 :     return NS_ERROR_OUT_OF_MEMORY;
     991             :   }
     992           0 :   aOutput.AppendLiteral("\" alt=\"\" >");
     993           0 :   return NS_OK;
     994             : }
     995             : 
     996           0 : NS_IMPL_ISUPPORTS(HTMLEditor::BlobReader, nsIEditorBlobListener)
     997             : 
     998           0 : HTMLEditor::BlobReader::BlobReader(BlobImpl* aBlob,
     999             :                                    HTMLEditor* aHTMLEditor,
    1000             :                                    bool aIsSafe,
    1001             :                                    nsIDOMDocument* aSourceDoc,
    1002             :                                    nsIDOMNode* aDestinationNode,
    1003             :                                    int32_t aDestOffset,
    1004           0 :                                    bool aDoDeleteSelection)
    1005             :   : mBlob(aBlob)
    1006             :   , mHTMLEditor(aHTMLEditor)
    1007             :   , mIsSafe(aIsSafe)
    1008             :   , mSourceDoc(aSourceDoc)
    1009             :   , mDestinationNode(aDestinationNode)
    1010             :   , mDestOffset(aDestOffset)
    1011           0 :   , mDoDeleteSelection(aDoDeleteSelection)
    1012             : {
    1013           0 :   MOZ_ASSERT(mBlob);
    1014           0 :   MOZ_ASSERT(mHTMLEditor);
    1015           0 :   MOZ_ASSERT(mDestinationNode);
    1016           0 : }
    1017             : 
    1018             : NS_IMETHODIMP
    1019           0 : HTMLEditor::BlobReader::OnResult(const nsACString& aResult)
    1020             : {
    1021           0 :   nsString blobType;
    1022           0 :   mBlob->GetType(blobType);
    1023             : 
    1024           0 :   NS_ConvertUTF16toUTF8 type(blobType);
    1025           0 :   nsAutoString stuffToPaste;
    1026           0 :   nsresult rv = ImgFromData(type, aResult, stuffToPaste);
    1027           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1028             : 
    1029           0 :   AutoEditBatch beginBatching(mHTMLEditor);
    1030           0 :   rv = mHTMLEditor->DoInsertHTMLWithContext(stuffToPaste, EmptyString(),
    1031           0 :                                             EmptyString(),
    1032           0 :                                             NS_LITERAL_STRING(kFileMime),
    1033             :                                             mSourceDoc,
    1034             :                                             mDestinationNode, mDestOffset,
    1035           0 :                                             mDoDeleteSelection,
    1036           0 :                                             mIsSafe, false);
    1037           0 :   return rv;
    1038             : }
    1039             : 
    1040             : NS_IMETHODIMP
    1041           0 : HTMLEditor::BlobReader::OnError(const nsAString& aError)
    1042             : {
    1043           0 :   nsCOMPtr<nsINode> destNode = do_QueryInterface(mDestinationNode);
    1044           0 :   const nsPromiseFlatString& flat = PromiseFlatString(aError);
    1045           0 :   const char16_t* error = flat.get();
    1046           0 :   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
    1047           0 :                                   NS_LITERAL_CSTRING("Editor"),
    1048           0 :                                   destNode->OwnerDoc(),
    1049             :                                   nsContentUtils::eDOM_PROPERTIES,
    1050             :                                   "EditorFileDropFailed",
    1051           0 :                                   &error, 1);
    1052           0 :   return NS_OK;
    1053             : }
    1054             : 
    1055             : nsresult
    1056           0 : HTMLEditor::InsertObject(const nsACString& aType,
    1057             :                          nsISupports* aObject,
    1058             :                          bool aIsSafe,
    1059             :                          nsIDOMDocument* aSourceDoc,
    1060             :                          nsIDOMNode* aDestinationNode,
    1061             :                          int32_t aDestOffset,
    1062             :                          bool aDoDeleteSelection)
    1063             : {
    1064             :   nsresult rv;
    1065             : 
    1066           0 :   if (nsCOMPtr<BlobImpl> blob = do_QueryInterface(aObject)) {
    1067             :     RefPtr<BlobReader> br = new BlobReader(blob, this, aIsSafe, aSourceDoc,
    1068             :                                            aDestinationNode, aDestOffset,
    1069           0 :                                            aDoDeleteSelection);
    1070             :     nsCOMPtr<nsIEditorUtils> utils =
    1071           0 :       do_GetService("@mozilla.org/editor-utils;1");
    1072           0 :     NS_ENSURE_TRUE(utils, NS_ERROR_FAILURE);
    1073             : 
    1074           0 :     nsCOMPtr<nsINode> node = do_QueryInterface(aDestinationNode);
    1075           0 :     MOZ_ASSERT(node);
    1076             : 
    1077           0 :     nsCOMPtr<nsIDOMBlob> domBlob = Blob::Create(node->GetOwnerGlobal(), blob);
    1078           0 :     NS_ENSURE_TRUE(domBlob, NS_ERROR_FAILURE);
    1079             : 
    1080           0 :     return utils->SlurpBlob(domBlob, node->OwnerDoc()->GetWindow(), br);
    1081             :   }
    1082             : 
    1083           0 :   nsAutoCString type(aType);
    1084             : 
    1085             :   // Check to see if we can insert an image file
    1086           0 :   bool insertAsImage = false;
    1087           0 :   nsCOMPtr<nsIFile> fileObj;
    1088           0 :   if (type.EqualsLiteral(kFileMime)) {
    1089           0 :     fileObj = do_QueryInterface(aObject);
    1090           0 :     if (fileObj) {
    1091             :       // Accept any image type fed to us
    1092           0 :       if (nsContentUtils::IsFileImage(fileObj, type)) {
    1093           0 :         insertAsImage = true;
    1094             :       } else {
    1095             :         // Reset type.
    1096           0 :         type.AssignLiteral(kFileMime);
    1097             :       }
    1098             :     }
    1099             :   }
    1100             : 
    1101           0 :   if (type.EqualsLiteral(kJPEGImageMime) ||
    1102           0 :       type.EqualsLiteral(kJPGImageMime) ||
    1103           0 :       type.EqualsLiteral(kPNGImageMime) ||
    1104           0 :       type.EqualsLiteral(kGIFImageMime) ||
    1105             :       insertAsImage) {
    1106           0 :     nsCString imageData;
    1107           0 :     if (insertAsImage) {
    1108           0 :       rv = nsContentUtils::SlurpFileToString(fileObj, imageData);
    1109           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1110             :     } else {
    1111           0 :       nsCOMPtr<nsIInputStream> imageStream = do_QueryInterface(aObject);
    1112           0 :       NS_ENSURE_TRUE(imageStream, NS_ERROR_FAILURE);
    1113             : 
    1114           0 :       rv = NS_ConsumeStream(imageStream, UINT32_MAX, imageData);
    1115           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1116             : 
    1117           0 :       rv = imageStream->Close();
    1118           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1119             :     }
    1120             : 
    1121           0 :     nsAutoString stuffToPaste;
    1122           0 :     rv = ImgFromData(type, imageData, stuffToPaste);
    1123           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1124             : 
    1125           0 :     AutoEditBatch beginBatching(this);
    1126           0 :     rv = DoInsertHTMLWithContext(stuffToPaste, EmptyString(), EmptyString(),
    1127           0 :                                  NS_LITERAL_STRING(kFileMime),
    1128             :                                  aSourceDoc,
    1129             :                                  aDestinationNode, aDestOffset,
    1130             :                                  aDoDeleteSelection,
    1131           0 :                                  aIsSafe, false);
    1132             :   }
    1133             : 
    1134           0 :   return NS_OK;
    1135             : }
    1136             : 
    1137             : nsresult
    1138           0 : HTMLEditor::InsertFromTransferable(nsITransferable* transferable,
    1139             :                                    nsIDOMDocument* aSourceDoc,
    1140             :                                    const nsAString& aContextStr,
    1141             :                                    const nsAString& aInfoStr,
    1142             :                                    bool havePrivateHTMLFlavor,
    1143             :                                    nsIDOMNode* aDestinationNode,
    1144             :                                    int32_t aDestOffset,
    1145             :                                    bool aDoDeleteSelection)
    1146             : {
    1147           0 :   nsresult rv = NS_OK;
    1148           0 :   nsAutoCString bestFlavor;
    1149           0 :   nsCOMPtr<nsISupports> genericDataObj;
    1150           0 :   uint32_t len = 0;
    1151           0 :   if (NS_SUCCEEDED(
    1152             :         transferable->GetAnyTransferData(bestFlavor,
    1153             :                                          getter_AddRefs(genericDataObj),
    1154             :                                          &len))) {
    1155           0 :     AutoTransactionsConserveSelection dontSpazMySelection(this);
    1156           0 :     nsAutoString flavor;
    1157           0 :     flavor.AssignWithConversion(bestFlavor);
    1158           0 :     nsAutoString stuffToPaste;
    1159           0 :     bool isSafe = IsSafeToInsertData(aSourceDoc);
    1160             : 
    1161           0 :     if (bestFlavor.EqualsLiteral(kFileMime) ||
    1162           0 :         bestFlavor.EqualsLiteral(kJPEGImageMime) ||
    1163           0 :         bestFlavor.EqualsLiteral(kJPGImageMime) ||
    1164           0 :         bestFlavor.EqualsLiteral(kPNGImageMime) ||
    1165           0 :         bestFlavor.EqualsLiteral(kGIFImageMime)) {
    1166           0 :       rv = InsertObject(bestFlavor, genericDataObj, isSafe,
    1167           0 :                         aSourceDoc, aDestinationNode, aDestOffset, aDoDeleteSelection);
    1168           0 :     } else if (bestFlavor.EqualsLiteral(kNativeHTMLMime)) {
    1169             :       // note cf_html uses utf8, hence use length = len, not len/2 as in flavors below
    1170           0 :       nsCOMPtr<nsISupportsCString> textDataObj = do_QueryInterface(genericDataObj);
    1171           0 :       if (textDataObj && len > 0) {
    1172           0 :         nsAutoCString cfhtml;
    1173           0 :         textDataObj->GetData(cfhtml);
    1174           0 :         NS_ASSERTION(cfhtml.Length() <= (len), "Invalid length!");
    1175           0 :         nsXPIDLString cfcontext, cffragment, cfselection; // cfselection left emtpy for now
    1176             : 
    1177           0 :         rv = ParseCFHTML(cfhtml, getter_Copies(cffragment), getter_Copies(cfcontext));
    1178           0 :         if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty()) {
    1179           0 :           AutoEditBatch beginBatching(this);
    1180             :           // If we have our private HTML flavor, we will only use the fragment
    1181             :           // from the CF_HTML. The rest comes from the clipboard.
    1182           0 :           if (havePrivateHTMLFlavor) {
    1183           0 :             rv = DoInsertHTMLWithContext(cffragment,
    1184             :                                          aContextStr, aInfoStr, flavor,
    1185             :                                          aSourceDoc,
    1186             :                                          aDestinationNode, aDestOffset,
    1187             :                                          aDoDeleteSelection,
    1188           0 :                                          isSafe);
    1189             :           } else {
    1190           0 :             rv = DoInsertHTMLWithContext(cffragment,
    1191             :                                          cfcontext, cfselection, flavor,
    1192             :                                          aSourceDoc,
    1193             :                                          aDestinationNode, aDestOffset,
    1194             :                                          aDoDeleteSelection,
    1195           0 :                                          isSafe);
    1196             : 
    1197             :           }
    1198             :         } else {
    1199             :           // In some platforms (like Linux), the clipboard might return data
    1200             :           // requested for unknown flavors (for example:
    1201             :           // application/x-moz-nativehtml).  In this case, treat the data
    1202             :           // to be pasted as mere HTML to get the best chance of pasting it
    1203             :           // correctly.
    1204           0 :           bestFlavor.AssignLiteral(kHTMLMime);
    1205             :           // Fall through the next case
    1206             :         }
    1207             :       }
    1208             :     }
    1209           0 :     if (bestFlavor.EqualsLiteral(kHTMLMime) ||
    1210           0 :         bestFlavor.EqualsLiteral(kUnicodeMime) ||
    1211           0 :         bestFlavor.EqualsLiteral(kMozTextInternal)) {
    1212           0 :       nsCOMPtr<nsISupportsString> textDataObj = do_QueryInterface(genericDataObj);
    1213           0 :       if (textDataObj && len > 0) {
    1214           0 :         nsAutoString text;
    1215           0 :         textDataObj->GetData(text);
    1216           0 :         NS_ASSERTION(text.Length() <= (len/2), "Invalid length!");
    1217           0 :         stuffToPaste.Assign(text.get(), len / 2);
    1218             :       } else {
    1219           0 :         nsCOMPtr<nsISupportsCString> textDataObj(do_QueryInterface(genericDataObj));
    1220           0 :         if (textDataObj && len > 0) {
    1221           0 :           nsAutoCString text;
    1222           0 :           textDataObj->GetData(text);
    1223           0 :           NS_ASSERTION(text.Length() <= len, "Invalid length!");
    1224           0 :           stuffToPaste.Assign(NS_ConvertUTF8toUTF16(Substring(text, 0, len)));
    1225             :         }
    1226             :       }
    1227             : 
    1228           0 :       if (!stuffToPaste.IsEmpty()) {
    1229           0 :         AutoEditBatch beginBatching(this);
    1230           0 :         if (bestFlavor.EqualsLiteral(kHTMLMime)) {
    1231           0 :           rv = DoInsertHTMLWithContext(stuffToPaste,
    1232             :                                        aContextStr, aInfoStr, flavor,
    1233             :                                        aSourceDoc,
    1234             :                                        aDestinationNode, aDestOffset,
    1235             :                                        aDoDeleteSelection,
    1236           0 :                                        isSafe);
    1237             :         } else {
    1238           0 :           rv = InsertTextAt(stuffToPaste, aDestinationNode, aDestOffset, aDoDeleteSelection);
    1239             :         }
    1240             :       }
    1241             :     }
    1242             :   }
    1243             : 
    1244             :   // Try to scroll the selection into view if the paste succeeded
    1245           0 :   if (NS_SUCCEEDED(rv)) {
    1246           0 :     ScrollSelectionIntoView(false);
    1247             :   }
    1248           0 :   return rv;
    1249             : }
    1250             : 
    1251             : static void
    1252           0 : GetStringFromDataTransfer(nsIDOMDataTransfer* aDataTransfer,
    1253             :                           const nsAString& aType,
    1254             :                           int32_t aIndex,
    1255             :                           nsAString& aOutputString)
    1256             : {
    1257           0 :   nsCOMPtr<nsIVariant> variant;
    1258           0 :   DataTransfer::Cast(aDataTransfer)->GetDataAtNoSecurityCheck(aType, aIndex, getter_AddRefs(variant));
    1259           0 :   if (variant) {
    1260           0 :     variant->GetAsAString(aOutputString);
    1261             :   }
    1262           0 : }
    1263             : 
    1264             : nsresult
    1265           0 : HTMLEditor::InsertFromDataTransfer(DataTransfer* aDataTransfer,
    1266             :                                    int32_t aIndex,
    1267             :                                    nsIDOMDocument* aSourceDoc,
    1268             :                                    nsIDOMNode* aDestinationNode,
    1269             :                                    int32_t aDestOffset,
    1270             :                                    bool aDoDeleteSelection)
    1271             : {
    1272           0 :   ErrorResult rv;
    1273             :   RefPtr<DOMStringList> types =
    1274           0 :     aDataTransfer->MozTypesAt(aIndex, CallerType::System, rv);
    1275           0 :   if (rv.Failed()) {
    1276           0 :     return rv.StealNSResult();
    1277             :   }
    1278             : 
    1279           0 :   bool hasPrivateHTMLFlavor = types->Contains(NS_LITERAL_STRING(kHTMLContext));
    1280             : 
    1281           0 :   bool isText = IsPlaintextEditor();
    1282           0 :   bool isSafe = IsSafeToInsertData(aSourceDoc);
    1283             : 
    1284           0 :   uint32_t length = types->Length();
    1285           0 :   for (uint32_t t = 0; t < length; t++) {
    1286           0 :     nsAutoString type;
    1287           0 :     types->Item(t, type);
    1288             : 
    1289           0 :     if (!isText) {
    1290           0 :       if (type.EqualsLiteral(kFileMime) ||
    1291           0 :           type.EqualsLiteral(kJPEGImageMime) ||
    1292           0 :           type.EqualsLiteral(kJPGImageMime) ||
    1293           0 :           type.EqualsLiteral(kPNGImageMime) ||
    1294           0 :           type.EqualsLiteral(kGIFImageMime)) {
    1295           0 :         nsCOMPtr<nsIVariant> variant;
    1296           0 :         DataTransfer::Cast(aDataTransfer)->GetDataAtNoSecurityCheck(type, aIndex, getter_AddRefs(variant));
    1297           0 :         if (variant) {
    1298           0 :           nsCOMPtr<nsISupports> object;
    1299           0 :           variant->GetAsISupports(getter_AddRefs(object));
    1300           0 :           return InsertObject(NS_ConvertUTF16toUTF8(type), object, isSafe,
    1301           0 :                               aSourceDoc, aDestinationNode, aDestOffset, aDoDeleteSelection);
    1302             :         }
    1303           0 :       } else if (type.EqualsLiteral(kNativeHTMLMime)) {
    1304             :         // Windows only clipboard parsing.
    1305           0 :         nsAutoString text;
    1306           0 :         GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
    1307           0 :         NS_ConvertUTF16toUTF8 cfhtml(text);
    1308             : 
    1309           0 :         nsXPIDLString cfcontext, cffragment, cfselection; // cfselection left emtpy for now
    1310             : 
    1311           0 :         nsresult rv = ParseCFHTML(cfhtml, getter_Copies(cffragment), getter_Copies(cfcontext));
    1312           0 :         if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty()) {
    1313           0 :           AutoEditBatch beginBatching(this);
    1314             : 
    1315           0 :           if (hasPrivateHTMLFlavor) {
    1316             :             // If we have our private HTML flavor, we will only use the fragment
    1317             :             // from the CF_HTML. The rest comes from the clipboard.
    1318           0 :             nsAutoString contextString, infoString;
    1319           0 :             GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), aIndex, contextString);
    1320           0 :             GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), aIndex, infoString);
    1321           0 :             return DoInsertHTMLWithContext(cffragment,
    1322             :                                            contextString, infoString, type,
    1323             :                                            aSourceDoc,
    1324             :                                            aDestinationNode, aDestOffset,
    1325             :                                            aDoDeleteSelection,
    1326           0 :                                            isSafe);
    1327             :           } else {
    1328           0 :             return DoInsertHTMLWithContext(cffragment,
    1329             :                                            cfcontext, cfselection, type,
    1330             :                                            aSourceDoc,
    1331             :                                            aDestinationNode, aDestOffset,
    1332             :                                            aDoDeleteSelection,
    1333           0 :                                            isSafe);
    1334             :           }
    1335             :         }
    1336           0 :       } else if (type.EqualsLiteral(kHTMLMime)) {
    1337           0 :         nsAutoString text, contextString, infoString;
    1338           0 :         GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
    1339           0 :         GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), aIndex, contextString);
    1340           0 :         GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), aIndex, infoString);
    1341             : 
    1342           0 :         AutoEditBatch beginBatching(this);
    1343           0 :         if (type.EqualsLiteral(kHTMLMime)) {
    1344           0 :           return DoInsertHTMLWithContext(text,
    1345             :                                          contextString, infoString, type,
    1346             :                                          aSourceDoc,
    1347             :                                          aDestinationNode, aDestOffset,
    1348             :                                          aDoDeleteSelection,
    1349           0 :                                          isSafe);
    1350             :         }
    1351             :       }
    1352             :     }
    1353             : 
    1354           0 :     if (type.EqualsLiteral(kTextMime) ||
    1355           0 :         type.EqualsLiteral(kMozTextInternal)) {
    1356           0 :       nsAutoString text;
    1357           0 :       GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
    1358             : 
    1359           0 :       AutoEditBatch beginBatching(this);
    1360           0 :       return InsertTextAt(text, aDestinationNode, aDestOffset, aDoDeleteSelection);
    1361             :     }
    1362             :   }
    1363             : 
    1364           0 :   return NS_OK;
    1365             : }
    1366             : 
    1367             : bool
    1368           0 : HTMLEditor::HavePrivateHTMLFlavor(nsIClipboard* aClipboard)
    1369             : {
    1370             :   // check the clipboard for our special kHTMLContext flavor.  If that is there, we know
    1371             :   // we have our own internal html format on clipboard.
    1372             : 
    1373           0 :   NS_ENSURE_TRUE(aClipboard, false);
    1374           0 :   bool bHavePrivateHTMLFlavor = false;
    1375             : 
    1376           0 :   const char* flavArray[] = { kHTMLContext };
    1377             : 
    1378           0 :   if (NS_SUCCEEDED(
    1379             :         aClipboard->HasDataMatchingFlavors(flavArray,
    1380             :                                            ArrayLength(flavArray),
    1381             :                                            nsIClipboard::kGlobalClipboard,
    1382             :                                            &bHavePrivateHTMLFlavor))) {
    1383           0 :     return bHavePrivateHTMLFlavor;
    1384             :   }
    1385             : 
    1386           0 :   return false;
    1387             : }
    1388             : 
    1389             : 
    1390             : NS_IMETHODIMP
    1391           0 : HTMLEditor::Paste(int32_t aSelectionType)
    1392             : {
    1393           0 :   if (!FireClipboardEvent(ePaste, aSelectionType)) {
    1394           0 :     return NS_OK;
    1395             :   }
    1396             : 
    1397             :   // Get Clipboard Service
    1398             :   nsresult rv;
    1399           0 :   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
    1400           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1401             : 
    1402             :   // Get the nsITransferable interface for getting the data from the clipboard
    1403           0 :   nsCOMPtr<nsITransferable> trans;
    1404           0 :   rv = PrepareHTMLTransferable(getter_AddRefs(trans));
    1405           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1406           0 :   NS_ENSURE_TRUE(trans, NS_ERROR_FAILURE);
    1407             :   // Get the Data from the clipboard
    1408           0 :   rv = clipboard->GetData(trans, aSelectionType);
    1409           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1410           0 :   if (!IsModifiable()) {
    1411           0 :     return NS_OK;
    1412             :   }
    1413             : 
    1414             :   // also get additional html copy hints, if present
    1415           0 :   nsAutoString contextStr, infoStr;
    1416             : 
    1417             :   // If we have our internal html flavor on the clipboard, there is special
    1418             :   // context to use instead of cfhtml context.
    1419           0 :   bool bHavePrivateHTMLFlavor = HavePrivateHTMLFlavor(clipboard);
    1420           0 :   if (bHavePrivateHTMLFlavor) {
    1421           0 :     nsCOMPtr<nsISupports> contextDataObj, infoDataObj;
    1422             :     uint32_t contextLen, infoLen;
    1423           0 :     nsCOMPtr<nsISupportsString> textDataObj;
    1424             : 
    1425             :     nsCOMPtr<nsITransferable> contextTrans =
    1426           0 :                   do_CreateInstance("@mozilla.org/widget/transferable;1");
    1427           0 :     NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER);
    1428           0 :     contextTrans->Init(nullptr);
    1429           0 :     contextTrans->AddDataFlavor(kHTMLContext);
    1430           0 :     clipboard->GetData(contextTrans, aSelectionType);
    1431           0 :     contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen);
    1432             : 
    1433             :     nsCOMPtr<nsITransferable> infoTrans =
    1434           0 :                   do_CreateInstance("@mozilla.org/widget/transferable;1");
    1435           0 :     NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER);
    1436           0 :     infoTrans->Init(nullptr);
    1437           0 :     infoTrans->AddDataFlavor(kHTMLInfo);
    1438           0 :     clipboard->GetData(infoTrans, aSelectionType);
    1439           0 :     infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen);
    1440             : 
    1441           0 :     if (contextDataObj) {
    1442           0 :       nsAutoString text;
    1443           0 :       textDataObj = do_QueryInterface(contextDataObj);
    1444           0 :       textDataObj->GetData(text);
    1445           0 :       NS_ASSERTION(text.Length() <= (contextLen/2), "Invalid length!");
    1446           0 :       contextStr.Assign(text.get(), contextLen / 2);
    1447             :     }
    1448             : 
    1449           0 :     if (infoDataObj) {
    1450           0 :       nsAutoString text;
    1451           0 :       textDataObj = do_QueryInterface(infoDataObj);
    1452           0 :       textDataObj->GetData(text);
    1453           0 :       NS_ASSERTION(text.Length() <= (infoLen/2), "Invalid length!");
    1454           0 :       infoStr.Assign(text.get(), infoLen / 2);
    1455             :     }
    1456             :   }
    1457             : 
    1458             :   // handle transferable hooks
    1459           0 :   nsCOMPtr<nsIDOMDocument> domdoc;
    1460           0 :   GetDocument(getter_AddRefs(domdoc));
    1461           0 :   if (!EditorHookUtils::DoInsertionHook(domdoc, nullptr, trans)) {
    1462           0 :     return NS_OK;
    1463             :   }
    1464             : 
    1465           0 :   return InsertFromTransferable(trans, nullptr, contextStr, infoStr, bHavePrivateHTMLFlavor,
    1466           0 :                                 nullptr, 0, true);
    1467             : }
    1468             : 
    1469             : NS_IMETHODIMP
    1470           0 : HTMLEditor::PasteTransferable(nsITransferable* aTransferable)
    1471             : {
    1472             :   // Use an invalid value for the clipboard type as data comes from aTransferable
    1473             :   // and we don't currently implement a way to put that in the data transfer yet.
    1474           0 :   if (!FireClipboardEvent(ePaste, nsIClipboard::kGlobalClipboard)) {
    1475           0 :     return NS_OK;
    1476             :   }
    1477             : 
    1478             :   // handle transferable hooks
    1479           0 :   nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
    1480           0 :   if (!EditorHookUtils::DoInsertionHook(domdoc, nullptr, aTransferable)) {
    1481           0 :     return NS_OK;
    1482             :   }
    1483             : 
    1484           0 :   nsAutoString contextStr, infoStr;
    1485             :   return InsertFromTransferable(aTransferable, nullptr, contextStr, infoStr, false,
    1486           0 :                                 nullptr, 0, true);
    1487             : }
    1488             : 
    1489             : /**
    1490             :  * HTML PasteNoFormatting. Ignore any HTML styles and formating in paste source.
    1491             :  */
    1492             : NS_IMETHODIMP
    1493           0 : HTMLEditor::PasteNoFormatting(int32_t aSelectionType)
    1494             : {
    1495           0 :   if (!FireClipboardEvent(ePasteNoFormatting, aSelectionType)) {
    1496           0 :     return NS_OK;
    1497             :   }
    1498             : 
    1499           0 :   ForceCompositionEnd();
    1500             : 
    1501             :   // Get Clipboard Service
    1502             :   nsresult rv;
    1503           0 :   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
    1504           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1505             : 
    1506             :   // Get the nsITransferable interface for getting the data from the clipboard.
    1507             :   // use TextEditor::PrepareTransferable() to force unicode plaintext data.
    1508           0 :   nsCOMPtr<nsITransferable> trans;
    1509           0 :   rv = TextEditor::PrepareTransferable(getter_AddRefs(trans));
    1510           0 :   if (NS_SUCCEEDED(rv) && trans) {
    1511             :     // Get the Data from the clipboard
    1512           0 :     if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) &&
    1513           0 :         IsModifiable()) {
    1514           0 :       const nsString& empty = EmptyString();
    1515           0 :       rv = InsertFromTransferable(trans, nullptr, empty, empty, false, nullptr, 0,
    1516             :                                   true);
    1517             :     }
    1518             :   }
    1519             : 
    1520           0 :   return rv;
    1521             : }
    1522             : 
    1523             : // The following arrays contain the MIME types that we can paste. The arrays
    1524             : // are used by CanPaste() and CanPasteTransferable() below.
    1525             : 
    1526             : static const char* textEditorFlavors[] = { kUnicodeMime };
    1527             : static const char* textHtmlEditorFlavors[] = { kUnicodeMime, kHTMLMime,
    1528             :                                                kJPEGImageMime, kJPGImageMime,
    1529             :                                                kPNGImageMime, kGIFImageMime };
    1530             : 
    1531             : NS_IMETHODIMP
    1532           0 : HTMLEditor::CanPaste(int32_t aSelectionType,
    1533             :                      bool* aCanPaste)
    1534             : {
    1535           0 :   NS_ENSURE_ARG_POINTER(aCanPaste);
    1536           0 :   *aCanPaste = false;
    1537             : 
    1538             :   // Always enable the paste command when inside of a HTML or XHTML document.
    1539           0 :   nsCOMPtr<nsIDocument> doc = GetDocument();
    1540           0 :   if (doc && doc->IsHTMLOrXHTML()) {
    1541           0 :     *aCanPaste = true;
    1542           0 :     return NS_OK;
    1543             :   }
    1544             : 
    1545             :   // can't paste if readonly
    1546           0 :   if (!IsModifiable()) {
    1547           0 :     return NS_OK;
    1548             :   }
    1549             : 
    1550             :   nsresult rv;
    1551           0 :   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
    1552           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1553             : 
    1554             :   bool haveFlavors;
    1555             : 
    1556             :   // Use the flavors depending on the current editor mask
    1557           0 :   if (IsPlaintextEditor()) {
    1558           0 :     rv = clipboard->HasDataMatchingFlavors(textEditorFlavors,
    1559           0 :                                            ArrayLength(textEditorFlavors),
    1560           0 :                                            aSelectionType, &haveFlavors);
    1561             :   } else {
    1562           0 :     rv = clipboard->HasDataMatchingFlavors(textHtmlEditorFlavors,
    1563           0 :                                            ArrayLength(textHtmlEditorFlavors),
    1564           0 :                                            aSelectionType, &haveFlavors);
    1565             :   }
    1566           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1567             : 
    1568           0 :   *aCanPaste = haveFlavors;
    1569           0 :   return NS_OK;
    1570             : }
    1571             : 
    1572             : NS_IMETHODIMP
    1573           0 : HTMLEditor::CanPasteTransferable(nsITransferable* aTransferable,
    1574             :                                  bool* aCanPaste)
    1575             : {
    1576           0 :   NS_ENSURE_ARG_POINTER(aCanPaste);
    1577             : 
    1578             :   // can't paste if readonly
    1579           0 :   if (!IsModifiable()) {
    1580           0 :     *aCanPaste = false;
    1581           0 :     return NS_OK;
    1582             :   }
    1583             : 
    1584             :   // If |aTransferable| is null, assume that a paste will succeed.
    1585           0 :   if (!aTransferable) {
    1586           0 :     *aCanPaste = true;
    1587           0 :     return NS_OK;
    1588             :   }
    1589             : 
    1590             :   // Peek in |aTransferable| to see if it contains a supported MIME type.
    1591             : 
    1592             :   // Use the flavors depending on the current editor mask
    1593             :   const char ** flavors;
    1594             :   unsigned length;
    1595           0 :   if (IsPlaintextEditor()) {
    1596           0 :     flavors = textEditorFlavors;
    1597           0 :     length = ArrayLength(textEditorFlavors);
    1598             :   } else {
    1599           0 :     flavors = textHtmlEditorFlavors;
    1600           0 :     length = ArrayLength(textHtmlEditorFlavors);
    1601             :   }
    1602             : 
    1603           0 :   for (unsigned int i = 0; i < length; i++, flavors++) {
    1604           0 :     nsCOMPtr<nsISupports> data;
    1605             :     uint32_t dataLen;
    1606           0 :     nsresult rv = aTransferable->GetTransferData(*flavors,
    1607           0 :                                                  getter_AddRefs(data),
    1608           0 :                                                  &dataLen);
    1609           0 :     if (NS_SUCCEEDED(rv) && data) {
    1610           0 :       *aCanPaste = true;
    1611           0 :       return NS_OK;
    1612             :     }
    1613             :   }
    1614             : 
    1615           0 :   *aCanPaste = false;
    1616           0 :   return NS_OK;
    1617             : }
    1618             : 
    1619             : /**
    1620             :  * HTML PasteAsQuotation: Paste in a blockquote type=cite.
    1621             :  */
    1622             : NS_IMETHODIMP
    1623           0 : HTMLEditor::PasteAsQuotation(int32_t aSelectionType)
    1624             : {
    1625           0 :   if (IsPlaintextEditor()) {
    1626           0 :     return PasteAsPlaintextQuotation(aSelectionType);
    1627             :   }
    1628             : 
    1629           0 :   nsAutoString citation;
    1630           0 :   return PasteAsCitedQuotation(citation, aSelectionType);
    1631             : }
    1632             : 
    1633             : NS_IMETHODIMP
    1634           0 : HTMLEditor::PasteAsCitedQuotation(const nsAString& aCitation,
    1635             :                                   int32_t aSelectionType)
    1636             : {
    1637           0 :   AutoEditBatch beginBatching(this);
    1638             :   AutoRules beginRulesSniffing(this, EditAction::insertQuotation,
    1639           0 :                                nsIEditor::eNext);
    1640             : 
    1641             :   // get selection
    1642           0 :   RefPtr<Selection> selection = GetSelection();
    1643           0 :   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
    1644             : 
    1645             :   // give rules a chance to handle or cancel
    1646           0 :   TextRulesInfo ruleInfo(EditAction::insertElement);
    1647             :   bool cancel, handled;
    1648             :   // Protect the edit rules object from dying
    1649           0 :   nsCOMPtr<nsIEditRules> rules(mRules);
    1650           0 :   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
    1651           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1652           0 :   if (cancel || handled) {
    1653           0 :     return NS_OK; // rules canceled the operation
    1654             :   }
    1655             : 
    1656             :   nsCOMPtr<Element> newNode =
    1657           0 :     DeleteSelectionAndCreateElement(*nsGkAtoms::blockquote);
    1658           0 :   NS_ENSURE_TRUE(newNode, NS_ERROR_NULL_POINTER);
    1659             : 
    1660             :   // Try to set type=cite.  Ignore it if this fails.
    1661           0 :   newNode->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
    1662           0 :                    NS_LITERAL_STRING("cite"), true);
    1663             : 
    1664             :   // Set the selection to the underneath the node we just inserted:
    1665           0 :   rv = selection->Collapse(newNode, 0);
    1666           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1667             : 
    1668           0 :   return Paste(aSelectionType);
    1669             : }
    1670             : 
    1671             : /**
    1672             :  * Paste a plaintext quotation.
    1673             :  */
    1674             : NS_IMETHODIMP
    1675           0 : HTMLEditor::PasteAsPlaintextQuotation(int32_t aSelectionType)
    1676             : {
    1677             :   // Get Clipboard Service
    1678             :   nsresult rv;
    1679           0 :   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
    1680           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1681             : 
    1682             :   // Create generic Transferable for getting the data
    1683             :   nsCOMPtr<nsITransferable> trans =
    1684           0 :                  do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
    1685           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1686           0 :   NS_ENSURE_TRUE(trans, NS_ERROR_FAILURE);
    1687             : 
    1688           0 :   nsCOMPtr<nsIDocument> destdoc = GetDocument();
    1689           0 :   nsILoadContext* loadContext = destdoc ? destdoc->GetLoadContext() : nullptr;
    1690           0 :   trans->Init(loadContext);
    1691             : 
    1692             :   // We only handle plaintext pastes here
    1693           0 :   trans->AddDataFlavor(kUnicodeMime);
    1694             : 
    1695             :   // Get the Data from the clipboard
    1696           0 :   clipboard->GetData(trans, aSelectionType);
    1697             : 
    1698             :   // Now we ask the transferable for the data
    1699             :   // it still owns the data, we just have a pointer to it.
    1700             :   // If it can't support a "text" output of the data the call will fail
    1701           0 :   nsCOMPtr<nsISupports> genericDataObj;
    1702           0 :   uint32_t len = 0;
    1703           0 :   nsAutoCString flav;
    1704           0 :   rv = trans->GetAnyTransferData(flav, getter_AddRefs(genericDataObj), &len);
    1705           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1706             : 
    1707           0 :   if (flav.EqualsLiteral(kUnicodeMime)) {
    1708           0 :     nsCOMPtr<nsISupportsString> textDataObj = do_QueryInterface(genericDataObj);
    1709           0 :     if (textDataObj && len > 0) {
    1710           0 :       nsAutoString stuffToPaste;
    1711           0 :       textDataObj->GetData(stuffToPaste);
    1712           0 :       NS_ASSERTION(stuffToPaste.Length() <= (len/2), "Invalid length!");
    1713           0 :       AutoEditBatch beginBatching(this);
    1714           0 :       rv = InsertAsPlaintextQuotation(stuffToPaste, true, 0);
    1715             :     }
    1716             :   }
    1717             : 
    1718           0 :   return rv;
    1719             : }
    1720             : 
    1721             : NS_IMETHODIMP
    1722           0 : HTMLEditor::InsertTextWithQuotations(const nsAString& aStringToInsert)
    1723             : {
    1724             :   // The whole operation should be undoable in one transaction:
    1725           0 :   BeginTransaction();
    1726             : 
    1727             :   // We're going to loop over the string, collecting up a "hunk"
    1728             :   // that's all the same type (quoted or not),
    1729             :   // Whenever the quotedness changes (or we reach the string's end)
    1730             :   // we will insert the hunk all at once, quoted or non.
    1731             : 
    1732             :   static const char16_t cite('>');
    1733           0 :   bool curHunkIsQuoted = (aStringToInsert.First() == cite);
    1734             : 
    1735           0 :   nsAString::const_iterator hunkStart, strEnd;
    1736           0 :   aStringToInsert.BeginReading(hunkStart);
    1737           0 :   aStringToInsert.EndReading(strEnd);
    1738             : 
    1739             :   // In the loop below, we only look for DOM newlines (\n),
    1740             :   // because we don't have a FindChars method that can look
    1741             :   // for both \r and \n.  \r is illegal in the dom anyway,
    1742             :   // but in debug builds, let's take the time to verify that
    1743             :   // there aren't any there:
    1744             : #ifdef DEBUG
    1745           0 :   nsAString::const_iterator dbgStart (hunkStart);
    1746           0 :   if (FindCharInReadable('\r', dbgStart, strEnd)) {
    1747           0 :     NS_ASSERTION(false,
    1748             :             "Return characters in DOM! InsertTextWithQuotations may be wrong");
    1749             :   }
    1750             : #endif /* DEBUG */
    1751             : 
    1752             :   // Loop over lines:
    1753           0 :   nsresult rv = NS_OK;
    1754           0 :   nsAString::const_iterator lineStart (hunkStart);
    1755             :   // We will break from inside when we run out of newlines.
    1756             :   for (;;) {
    1757             :     // Search for the end of this line (dom newlines, see above):
    1758           0 :     bool found = FindCharInReadable('\n', lineStart, strEnd);
    1759           0 :     bool quoted = false;
    1760           0 :     if (found) {
    1761             :       // if there's another newline, lineStart now points there.
    1762             :       // Loop over any consecutive newline chars:
    1763           0 :       nsAString::const_iterator firstNewline (lineStart);
    1764           0 :       while (*lineStart == '\n') {
    1765           0 :         ++lineStart;
    1766             :       }
    1767           0 :       quoted = (*lineStart == cite);
    1768           0 :       if (quoted == curHunkIsQuoted) {
    1769           0 :         continue;
    1770             :       }
    1771             :       // else we're changing state, so we need to insert
    1772             :       // from curHunk to lineStart then loop around.
    1773             : 
    1774             :       // But if the current hunk is quoted, then we want to make sure
    1775             :       // that any extra newlines on the end do not get included in
    1776             :       // the quoted section: blank lines flaking a quoted section
    1777             :       // should be considered unquoted, so that if the user clicks
    1778             :       // there and starts typing, the new text will be outside of
    1779             :       // the quoted block.
    1780           0 :       if (curHunkIsQuoted) {
    1781           0 :         lineStart = firstNewline;
    1782             : 
    1783             :         // 'firstNewline' points to the first '\n'. We want to
    1784             :         // ensure that this first newline goes into the hunk
    1785             :         // since quoted hunks can be displayed as blocks
    1786             :         // (and the newline should become invisible in this case).
    1787             :         // So the next line needs to start at the next character.
    1788           0 :         lineStart++;
    1789             :       }
    1790             :     }
    1791             : 
    1792             :     // If no newline found, lineStart is now strEnd and we can finish up,
    1793             :     // inserting from curHunk to lineStart then returning.
    1794           0 :     const nsAString &curHunk = Substring(hunkStart, lineStart);
    1795           0 :     nsCOMPtr<nsIDOMNode> dummyNode;
    1796           0 :     if (curHunkIsQuoted) {
    1797           0 :       rv = InsertAsPlaintextQuotation(curHunk, false,
    1798           0 :                                       getter_AddRefs(dummyNode));
    1799             :     } else {
    1800           0 :       rv = InsertText(curHunk);
    1801             :     }
    1802           0 :     if (!found) {
    1803           0 :       break;
    1804             :     }
    1805           0 :     curHunkIsQuoted = quoted;
    1806           0 :     hunkStart = lineStart;
    1807           0 :   }
    1808             : 
    1809           0 :   EndTransaction();
    1810             : 
    1811           0 :   return rv;
    1812             : }
    1813             : 
    1814             : NS_IMETHODIMP
    1815           0 : HTMLEditor::InsertAsQuotation(const nsAString& aQuotedText,
    1816             :                               nsIDOMNode** aNodeInserted)
    1817             : {
    1818           0 :   if (IsPlaintextEditor()) {
    1819           0 :     return InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted);
    1820             :   }
    1821             : 
    1822           0 :   nsAutoString citation;
    1823             :   return InsertAsCitedQuotation(aQuotedText, citation, false,
    1824           0 :                                 aNodeInserted);
    1825             : }
    1826             : 
    1827             : // Insert plaintext as a quotation, with cite marks (e.g. "> ").
    1828             : // This differs from its corresponding method in TextEditor
    1829             : // in that here, quoted material is enclosed in a <pre> tag
    1830             : // in order to preserve the original line wrapping.
    1831             : NS_IMETHODIMP
    1832           0 : HTMLEditor::InsertAsPlaintextQuotation(const nsAString& aQuotedText,
    1833             :                                        bool aAddCites,
    1834             :                                        nsIDOMNode** aNodeInserted)
    1835             : {
    1836             :   // get selection
    1837           0 :   RefPtr<Selection> selection = GetSelection();
    1838           0 :   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
    1839             : 
    1840           0 :   AutoEditBatch beginBatching(this);
    1841             :   AutoRules beginRulesSniffing(this, EditAction::insertQuotation,
    1842           0 :                                nsIEditor::eNext);
    1843             : 
    1844             :   // give rules a chance to handle or cancel
    1845           0 :   TextRulesInfo ruleInfo(EditAction::insertElement);
    1846             :   bool cancel, handled;
    1847             :   // Protect the edit rules object from dying
    1848           0 :   nsCOMPtr<nsIEditRules> rules(mRules);
    1849           0 :   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
    1850           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1851           0 :   if (cancel || handled) {
    1852           0 :     return NS_OK; // rules canceled the operation
    1853             :   }
    1854             : 
    1855             :   // Wrap the inserted quote in a <span> so we can distinguish it. If we're
    1856             :   // inserting into the <body>, we use a <span> which is displayed as a block
    1857             :   // and sized to the screen using 98 viewport width units.
    1858             :   // We could use 100vw, but 98vw avoids a horizontal scroll bar where possible.
    1859             :   // All this is done to wrap overlong lines to the screen and not to the
    1860             :   // container element, the width-restricted body.
    1861             :   nsCOMPtr<Element> newNode =
    1862           0 :     DeleteSelectionAndCreateElement(*nsGkAtoms::span);
    1863             : 
    1864             :   // If this succeeded, then set selection inside the pre
    1865             :   // so the inserted text will end up there.
    1866             :   // If it failed, we don't care what the return value was,
    1867             :   // but we'll fall through and try to insert the text anyway.
    1868           0 :   if (newNode) {
    1869             :     // Add an attribute on the pre node so we'll know it's a quotation.
    1870           0 :     newNode->SetAttr(kNameSpaceID_None, nsGkAtoms::mozquote,
    1871           0 :                      NS_LITERAL_STRING("true"), true);
    1872             :     // Allow wrapping on spans so long lines get wrapped to the screen.
    1873           0 :     nsCOMPtr<nsINode> parent = newNode->GetParentNode();
    1874           0 :     if (parent && parent->IsHTMLElement(nsGkAtoms::body)) {
    1875           0 :       newNode->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
    1876           0 :         NS_LITERAL_STRING("white-space: pre-wrap; display: block; width: 98vw;"),
    1877           0 :         true);
    1878             :     } else {
    1879           0 :       newNode->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
    1880           0 :         NS_LITERAL_STRING("white-space: pre-wrap;"), true);
    1881             :     }
    1882             : 
    1883             :     // and set the selection inside it:
    1884           0 :     selection->Collapse(newNode, 0);
    1885             :   }
    1886             : 
    1887           0 :   if (aAddCites) {
    1888           0 :     rv = TextEditor::InsertAsQuotation(aQuotedText, aNodeInserted);
    1889             :   } else {
    1890           0 :     rv = TextEditor::InsertText(aQuotedText);
    1891             :   }
    1892             :   // Note that if !aAddCites, aNodeInserted isn't set.
    1893             :   // That's okay because the routines that use aAddCites
    1894             :   // don't need to know the inserted node.
    1895             : 
    1896           0 :   if (aNodeInserted && NS_SUCCEEDED(rv)) {
    1897           0 :     *aNodeInserted = GetAsDOMNode(newNode);
    1898           0 :     NS_IF_ADDREF(*aNodeInserted);
    1899             :   }
    1900             : 
    1901             :   // Set the selection to just after the inserted node:
    1902           0 :   if (NS_SUCCEEDED(rv) && newNode) {
    1903           0 :     nsCOMPtr<nsINode> parent = newNode->GetParentNode();
    1904           0 :     int32_t offset = parent ? parent->IndexOf(newNode) : -1;
    1905           0 :     if (parent) {
    1906           0 :       selection->Collapse(parent, offset + 1);
    1907             :     }
    1908             :   }
    1909           0 :   return rv;
    1910             : }
    1911             : 
    1912             : NS_IMETHODIMP
    1913           0 : HTMLEditor::StripCites()
    1914             : {
    1915           0 :   return TextEditor::StripCites();
    1916             : }
    1917             : 
    1918             : NS_IMETHODIMP
    1919           0 : HTMLEditor::Rewrap(bool aRespectNewlines)
    1920             : {
    1921           0 :   return TextEditor::Rewrap(aRespectNewlines);
    1922             : }
    1923             : 
    1924             : NS_IMETHODIMP
    1925           0 : HTMLEditor::InsertAsCitedQuotation(const nsAString& aQuotedText,
    1926             :                                    const nsAString& aCitation,
    1927             :                                    bool aInsertHTML,
    1928             :                                    nsIDOMNode** aNodeInserted)
    1929             : {
    1930             :   // Don't let anyone insert html into a "plaintext" editor:
    1931           0 :   if (IsPlaintextEditor()) {
    1932           0 :     NS_ASSERTION(!aInsertHTML, "InsertAsCitedQuotation: trying to insert html into plaintext editor");
    1933           0 :     return InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted);
    1934             :   }
    1935             : 
    1936             :   // get selection
    1937           0 :   RefPtr<Selection> selection = GetSelection();
    1938           0 :   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
    1939             : 
    1940           0 :   AutoEditBatch beginBatching(this);
    1941             :   AutoRules beginRulesSniffing(this, EditAction::insertQuotation,
    1942           0 :                                nsIEditor::eNext);
    1943             : 
    1944             :   // give rules a chance to handle or cancel
    1945           0 :   TextRulesInfo ruleInfo(EditAction::insertElement);
    1946             :   bool cancel, handled;
    1947             :   // Protect the edit rules object from dying
    1948           0 :   nsCOMPtr<nsIEditRules> rules(mRules);
    1949           0 :   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
    1950           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1951           0 :   if (cancel || handled) {
    1952           0 :     return NS_OK; // rules canceled the operation
    1953             :   }
    1954             : 
    1955             :   nsCOMPtr<Element> newNode =
    1956           0 :     DeleteSelectionAndCreateElement(*nsGkAtoms::blockquote);
    1957           0 :   NS_ENSURE_TRUE(newNode, NS_ERROR_NULL_POINTER);
    1958             : 
    1959             :   // Try to set type=cite.  Ignore it if this fails.
    1960           0 :   newNode->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
    1961           0 :                    NS_LITERAL_STRING("cite"), true);
    1962             : 
    1963           0 :   if (!aCitation.IsEmpty()) {
    1964           0 :     newNode->SetAttr(kNameSpaceID_None, nsGkAtoms::cite, aCitation, true);
    1965             :   }
    1966             : 
    1967             :   // Set the selection inside the blockquote so aQuotedText will go there:
    1968           0 :   selection->Collapse(newNode, 0);
    1969             : 
    1970           0 :   if (aInsertHTML) {
    1971           0 :     rv = LoadHTML(aQuotedText);
    1972             :   } else {
    1973           0 :     rv = InsertText(aQuotedText);  // XXX ignore charset
    1974             :   }
    1975             : 
    1976           0 :   if (aNodeInserted && NS_SUCCEEDED(rv)) {
    1977           0 :     *aNodeInserted = GetAsDOMNode(newNode);
    1978           0 :     NS_IF_ADDREF(*aNodeInserted);
    1979             :   }
    1980             : 
    1981             :   // Set the selection to just after the inserted node:
    1982           0 :   if (NS_SUCCEEDED(rv) && newNode) {
    1983           0 :     nsCOMPtr<nsINode> parent = newNode->GetParentNode();
    1984           0 :     int32_t offset = parent ? parent->IndexOf(newNode) : -1;
    1985           0 :     if (parent) {
    1986           0 :       selection->Collapse(parent, offset + 1);
    1987             :     }
    1988             :   }
    1989           0 :   return rv;
    1990             : }
    1991             : 
    1992             : 
    1993           0 : void RemoveBodyAndHead(nsINode& aNode)
    1994             : {
    1995           0 :   nsCOMPtr<nsIContent> body, head;
    1996             :   // find the body and head nodes if any.
    1997             :   // look only at immediate children of aNode.
    1998           0 :   for (nsCOMPtr<nsIContent> child = aNode.GetFirstChild();
    1999             :        child;
    2000           0 :        child = child->GetNextSibling()) {
    2001           0 :     if (child->IsHTMLElement(nsGkAtoms::body)) {
    2002           0 :       body = child;
    2003           0 :     } else if (child->IsHTMLElement(nsGkAtoms::head)) {
    2004           0 :       head = child;
    2005             :     }
    2006             :   }
    2007           0 :   if (head) {
    2008           0 :     ErrorResult ignored;
    2009           0 :     aNode.RemoveChild(*head, ignored);
    2010             :   }
    2011           0 :   if (body) {
    2012           0 :     nsCOMPtr<nsIContent> child = body->GetFirstChild();
    2013           0 :     while (child) {
    2014           0 :       ErrorResult ignored;
    2015           0 :       aNode.InsertBefore(*child, body, ignored);
    2016           0 :       child = body->GetFirstChild();
    2017             :     }
    2018             : 
    2019           0 :     ErrorResult ignored;
    2020           0 :     aNode.RemoveChild(*body, ignored);
    2021             :   }
    2022           0 : }
    2023             : 
    2024             : /**
    2025             :  * This function finds the target node that we will be pasting into. aStart is
    2026             :  * the context that we're given and aResult will be the target. Initially,
    2027             :  * *aResult must be nullptr.
    2028             :  *
    2029             :  * The target for a paste is found by either finding the node that contains
    2030             :  * the magical comment node containing kInsertCookie or, failing that, the
    2031             :  * firstChild of the firstChild (until we reach a leaf).
    2032             :  */
    2033           0 : nsresult FindTargetNode(nsIDOMNode *aStart, nsCOMPtr<nsIDOMNode> &aResult)
    2034             : {
    2035           0 :   NS_ENSURE_TRUE(aStart, NS_OK);
    2036             : 
    2037           0 :   nsCOMPtr<nsIDOMNode> child, tmp;
    2038             : 
    2039           0 :   nsresult rv = aStart->GetFirstChild(getter_AddRefs(child));
    2040           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2041             : 
    2042           0 :   if (!child) {
    2043             :     // If the current result is nullptr, then aStart is a leaf, and is the
    2044             :     // fallback result.
    2045           0 :     if (!aResult) {
    2046           0 :       aResult = aStart;
    2047             :     }
    2048           0 :     return NS_OK;
    2049             :   }
    2050             : 
    2051           0 :   do {
    2052             :     // Is this child the magical cookie?
    2053           0 :     nsCOMPtr<nsIDOMComment> comment = do_QueryInterface(child);
    2054           0 :     if (comment) {
    2055           0 :       nsAutoString data;
    2056           0 :       rv = comment->GetData(data);
    2057           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2058             : 
    2059           0 :       if (data.EqualsLiteral(kInsertCookie)) {
    2060             :         // Yes it is! Return an error so we bubble out and short-circuit the
    2061             :         // search.
    2062           0 :         aResult = aStart;
    2063             : 
    2064             :         // Note: it doesn't matter if this fails.
    2065           0 :         aStart->RemoveChild(child, getter_AddRefs(tmp));
    2066             : 
    2067           0 :         return NS_SUCCESS_EDITOR_FOUND_TARGET;
    2068             :       }
    2069             :     }
    2070             : 
    2071           0 :     rv = FindTargetNode(child, aResult);
    2072           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2073             : 
    2074           0 :     if (rv == NS_SUCCESS_EDITOR_FOUND_TARGET) {
    2075           0 :       return NS_SUCCESS_EDITOR_FOUND_TARGET;
    2076             :     }
    2077             : 
    2078           0 :     rv = child->GetNextSibling(getter_AddRefs(tmp));
    2079           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2080             : 
    2081           0 :     child = tmp;
    2082             :   } while (child);
    2083             : 
    2084           0 :   return NS_OK;
    2085             : }
    2086             : 
    2087             : nsresult
    2088           0 : HTMLEditor::CreateDOMFragmentFromPaste(const nsAString& aInputString,
    2089             :                                        const nsAString& aContextStr,
    2090             :                                        const nsAString& aInfoStr,
    2091             :                                        nsCOMPtr<nsIDOMNode>* outFragNode,
    2092             :                                        nsCOMPtr<nsIDOMNode>* outStartNode,
    2093             :                                        nsCOMPtr<nsIDOMNode>* outEndNode,
    2094             :                                        int32_t* outStartOffset,
    2095             :                                        int32_t* outEndOffset,
    2096             :                                        bool aTrustedInput)
    2097             : {
    2098           0 :   NS_ENSURE_TRUE(outFragNode && outStartNode && outEndNode, NS_ERROR_NULL_POINTER);
    2099             : 
    2100           0 :   nsCOMPtr<nsIDocument> doc = GetDocument();
    2101           0 :   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
    2102             : 
    2103             :   // if we have context info, create a fragment for that
    2104           0 :   nsresult rv = NS_OK;
    2105           0 :   nsCOMPtr<nsIDOMNode> contextLeaf;
    2106           0 :   RefPtr<DocumentFragment> contextAsNode;
    2107           0 :   if (!aContextStr.IsEmpty()) {
    2108           0 :     rv = ParseFragment(aContextStr, nullptr, doc, getter_AddRefs(contextAsNode),
    2109           0 :                        aTrustedInput);
    2110           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2111           0 :     NS_ENSURE_TRUE(contextAsNode, NS_ERROR_FAILURE);
    2112             : 
    2113           0 :     rv = StripFormattingNodes(*contextAsNode);
    2114           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2115             : 
    2116           0 :     RemoveBodyAndHead(*contextAsNode);
    2117             : 
    2118           0 :     rv = FindTargetNode(contextAsNode, contextLeaf);
    2119           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2120             :   }
    2121             : 
    2122           0 :   nsCOMPtr<nsIContent> contextLeafAsContent = do_QueryInterface(contextLeaf);
    2123           0 :   MOZ_ASSERT_IF(contextLeaf, contextLeafAsContent);
    2124             : 
    2125             :   // create fragment for pasted html
    2126             :   nsIAtom* contextAtom;
    2127           0 :   if (contextLeafAsContent) {
    2128           0 :     contextAtom = contextLeafAsContent->NodeInfo()->NameAtom();
    2129           0 :     if (contextLeafAsContent->IsHTMLElement(nsGkAtoms::html)) {
    2130           0 :       contextAtom = nsGkAtoms::body;
    2131             :     }
    2132             :   } else {
    2133           0 :     contextAtom = nsGkAtoms::body;
    2134             :   }
    2135           0 :   RefPtr<DocumentFragment> fragment;
    2136           0 :   rv = ParseFragment(aInputString,
    2137             :                      contextAtom,
    2138             :                      doc,
    2139           0 :                      getter_AddRefs(fragment),
    2140           0 :                      aTrustedInput);
    2141           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2142           0 :   NS_ENSURE_TRUE(fragment, NS_ERROR_FAILURE);
    2143             : 
    2144           0 :   RemoveBodyAndHead(*fragment);
    2145             : 
    2146           0 :   if (contextAsNode) {
    2147             :     // unite the two trees
    2148           0 :     IgnoredErrorResult ignored;
    2149           0 :     contextLeafAsContent->AppendChild(*fragment, ignored);
    2150           0 :     fragment = contextAsNode;
    2151             :   }
    2152             : 
    2153           0 :   rv = StripFormattingNodes(*fragment, true);
    2154           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2155             : 
    2156             :   // If there was no context, then treat all of the data we did get as the
    2157             :   // pasted data.
    2158           0 :   if (contextLeaf) {
    2159           0 :     *outEndNode = *outStartNode = contextLeaf;
    2160             :   } else {
    2161           0 :     *outEndNode = *outStartNode = fragment;
    2162             :   }
    2163             : 
    2164           0 :   *outFragNode = fragment.forget();
    2165           0 :   *outStartOffset = 0;
    2166             : 
    2167             :   // get the infoString contents
    2168           0 :   if (!aInfoStr.IsEmpty()) {
    2169           0 :     int32_t sep = aInfoStr.FindChar((char16_t)',');
    2170           0 :     nsAutoString numstr1(Substring(aInfoStr, 0, sep));
    2171           0 :     nsAutoString numstr2(Substring(aInfoStr, sep+1, aInfoStr.Length() - (sep+1)));
    2172             : 
    2173             :     // Move the start and end children.
    2174             :     nsresult err;
    2175           0 :     int32_t num = numstr1.ToInteger(&err);
    2176             : 
    2177           0 :     nsCOMPtr<nsIDOMNode> tmp;
    2178           0 :     while (num--) {
    2179           0 :       (*outStartNode)->GetFirstChild(getter_AddRefs(tmp));
    2180           0 :       NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
    2181           0 :       tmp.swap(*outStartNode);
    2182             :     }
    2183             : 
    2184           0 :     num = numstr2.ToInteger(&err);
    2185           0 :     while (num--) {
    2186           0 :       (*outEndNode)->GetLastChild(getter_AddRefs(tmp));
    2187           0 :       NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
    2188           0 :       tmp.swap(*outEndNode);
    2189             :     }
    2190             :   }
    2191             : 
    2192           0 :   nsCOMPtr<nsINode> node = do_QueryInterface(*outEndNode);
    2193           0 :   *outEndOffset = node->Length();
    2194           0 :   return NS_OK;
    2195             : }
    2196             : 
    2197             : 
    2198             : nsresult
    2199           0 : HTMLEditor::ParseFragment(const nsAString& aFragStr,
    2200             :                           nsIAtom* aContextLocalName,
    2201             :                           nsIDocument* aTargetDocument,
    2202             :                           DocumentFragment** aFragment,
    2203             :                           bool aTrustedInput)
    2204             : {
    2205           0 :   nsAutoScriptBlockerSuppressNodeRemoved autoBlocker;
    2206             : 
    2207             :   RefPtr<DocumentFragment> fragment =
    2208           0 :     new DocumentFragment(aTargetDocument->NodeInfoManager());
    2209           0 :   nsresult rv = nsContentUtils::ParseFragmentHTML(aFragStr,
    2210             :                                                   fragment,
    2211             :                                                   aContextLocalName ?
    2212             :                                                     aContextLocalName : nsGkAtoms::body,
    2213             :                                                     kNameSpaceID_XHTML,
    2214             :                                                   false,
    2215           0 :                                                   true);
    2216           0 :   if (!aTrustedInput) {
    2217             :     nsTreeSanitizer sanitizer(aContextLocalName ?
    2218             :                               nsIParserUtils::SanitizerAllowStyle :
    2219           0 :                               nsIParserUtils::SanitizerAllowComments);
    2220           0 :     sanitizer.Sanitize(fragment);
    2221             :   }
    2222           0 :   fragment.forget(aFragment);
    2223           0 :   return rv;
    2224             : }
    2225             : 
    2226             : void
    2227           0 : HTMLEditor::CreateListOfNodesToPaste(
    2228             :               DocumentFragment& aFragment,
    2229             :               nsTArray<OwningNonNull<nsINode>>& outNodeList,
    2230             :               nsINode* aStartContainer,
    2231             :               int32_t aStartOffset,
    2232             :               nsINode* aEndContainer,
    2233             :               int32_t aEndOffset)
    2234             : {
    2235             :   // If no info was provided about the boundary between context and stream,
    2236             :   // then assume all is stream.
    2237           0 :   if (!aStartContainer) {
    2238           0 :     aStartContainer = &aFragment;
    2239           0 :     aStartOffset = 0;
    2240           0 :     aEndContainer = &aFragment;
    2241           0 :     aEndOffset = aFragment.Length();
    2242             :   }
    2243             : 
    2244           0 :   RefPtr<nsRange> docFragRange;
    2245           0 :   nsresult rv = nsRange::CreateRange(aStartContainer, aStartOffset,
    2246             :                                      aEndContainer, aEndOffset,
    2247           0 :                                      getter_AddRefs(docFragRange));
    2248           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    2249           0 :   NS_ENSURE_SUCCESS(rv, );
    2250             : 
    2251             :   // Now use a subtree iterator over the range to create a list of nodes
    2252           0 :   TrivialFunctor functor;
    2253           0 :   DOMSubtreeIterator iter;
    2254           0 :   rv = iter.Init(*docFragRange);
    2255           0 :   NS_ENSURE_SUCCESS(rv, );
    2256           0 :   iter.AppendList(functor, outNodeList);
    2257             : }
    2258             : 
    2259             : void
    2260           0 : HTMLEditor::GetListAndTableParents(StartOrEnd aStartOrEnd,
    2261             :                                    nsTArray<OwningNonNull<nsINode>>& aNodeList,
    2262             :                                    nsTArray<OwningNonNull<Element>>& outArray)
    2263             : {
    2264           0 :   MOZ_ASSERT(aNodeList.Length());
    2265             : 
    2266             :   // Build up list of parents of first (or last) node in list that are either
    2267             :   // lists, or tables.
    2268           0 :   int32_t idx = aStartOrEnd == StartOrEnd::end ? aNodeList.Length() - 1 : 0;
    2269             : 
    2270           0 :   for (nsCOMPtr<nsINode> node = aNodeList[idx]; node;
    2271           0 :        node = node->GetParentNode()) {
    2272           0 :     if (HTMLEditUtils::IsList(node) || HTMLEditUtils::IsTable(node)) {
    2273           0 :       outArray.AppendElement(*node->AsElement());
    2274             :     }
    2275             :   }
    2276           0 : }
    2277             : 
    2278             : int32_t
    2279           0 : HTMLEditor::DiscoverPartialListsAndTables(
    2280             :               nsTArray<OwningNonNull<nsINode>>& aPasteNodes,
    2281             :               nsTArray<OwningNonNull<Element>>& aListsAndTables)
    2282             : {
    2283           0 :   int32_t ret = -1;
    2284           0 :   int32_t listAndTableParents = aListsAndTables.Length();
    2285             : 
    2286             :   // Scan insertion list for table elements (other than table).
    2287           0 :   for (auto& curNode : aPasteNodes) {
    2288           0 :     if (HTMLEditUtils::IsTableElement(curNode) &&
    2289           0 :         !curNode->IsHTMLElement(nsGkAtoms::table)) {
    2290           0 :       nsCOMPtr<Element> table = curNode->GetParentElement();
    2291           0 :       while (table && !table->IsHTMLElement(nsGkAtoms::table)) {
    2292           0 :         table = table->GetParentElement();
    2293             :       }
    2294           0 :       if (table) {
    2295           0 :         int32_t idx = aListsAndTables.IndexOf(table);
    2296           0 :         if (idx == -1) {
    2297           0 :           return ret;
    2298             :         }
    2299           0 :         ret = idx;
    2300           0 :         if (ret == listAndTableParents - 1) {
    2301           0 :           return ret;
    2302             :         }
    2303             :       }
    2304             :     }
    2305           0 :     if (HTMLEditUtils::IsListItem(curNode)) {
    2306           0 :       nsCOMPtr<Element> list = curNode->GetParentElement();
    2307           0 :       while (list && !HTMLEditUtils::IsList(list)) {
    2308           0 :         list = list->GetParentElement();
    2309             :       }
    2310           0 :       if (list) {
    2311           0 :         int32_t idx = aListsAndTables.IndexOf(list);
    2312           0 :         if (idx == -1) {
    2313           0 :           return ret;
    2314             :         }
    2315           0 :         ret = idx;
    2316           0 :         if (ret == listAndTableParents - 1) {
    2317           0 :           return ret;
    2318             :         }
    2319             :       }
    2320             :     }
    2321             :   }
    2322           0 :   return ret;
    2323             : }
    2324             : 
    2325             : nsINode*
    2326           0 : HTMLEditor::ScanForListAndTableStructure(
    2327             :               StartOrEnd aStartOrEnd,
    2328             :               nsTArray<OwningNonNull<nsINode>>& aNodes,
    2329             :               Element& aListOrTable)
    2330             : {
    2331             :   // Look upward from first/last paste node for a piece of this list/table
    2332           0 :   int32_t idx = aStartOrEnd == StartOrEnd::end ? aNodes.Length() - 1 : 0;
    2333           0 :   bool isList = HTMLEditUtils::IsList(&aListOrTable);
    2334             : 
    2335           0 :   for (nsCOMPtr<nsINode> node = aNodes[idx]; node;
    2336           0 :        node = node->GetParentNode()) {
    2337           0 :     if ((isList && HTMLEditUtils::IsListItem(node)) ||
    2338           0 :         (!isList && HTMLEditUtils::IsTableElement(node) &&
    2339           0 :                     !node->IsHTMLElement(nsGkAtoms::table))) {
    2340           0 :       nsCOMPtr<Element> structureNode = node->GetParentElement();
    2341           0 :       if (isList) {
    2342           0 :         while (structureNode && !HTMLEditUtils::IsList(structureNode)) {
    2343           0 :           structureNode = structureNode->GetParentElement();
    2344             :         }
    2345             :       } else {
    2346           0 :         while (structureNode &&
    2347           0 :                !structureNode->IsHTMLElement(nsGkAtoms::table)) {
    2348           0 :           structureNode = structureNode->GetParentElement();
    2349             :         }
    2350             :       }
    2351           0 :       if (structureNode == &aListOrTable) {
    2352           0 :         if (isList) {
    2353           0 :           return structureNode;
    2354             :         }
    2355           0 :         return node;
    2356             :       }
    2357             :     }
    2358             :   }
    2359           0 :   return nullptr;
    2360             : }
    2361             : 
    2362             : void
    2363           0 : HTMLEditor::ReplaceOrphanedStructure(
    2364             :               StartOrEnd aStartOrEnd,
    2365             :               nsTArray<OwningNonNull<nsINode>>& aNodeArray,
    2366             :               nsTArray<OwningNonNull<Element>>& aListAndTableArray,
    2367             :               int32_t aHighWaterMark)
    2368             : {
    2369           0 :   OwningNonNull<Element> curNode = aListAndTableArray[aHighWaterMark];
    2370             : 
    2371             :   // Find substructure of list or table that must be included in paste.
    2372             :   nsCOMPtr<nsINode> replaceNode =
    2373           0 :     ScanForListAndTableStructure(aStartOrEnd, aNodeArray, curNode);
    2374             : 
    2375           0 :   if (!replaceNode) {
    2376           0 :     return;
    2377             :   }
    2378             : 
    2379             :   // If we found substructure, paste it instead of its descendants.
    2380             :   // Postprocess list to remove any descendants of this node so that we don't
    2381             :   // insert them twice.
    2382           0 :   uint32_t removedCount = 0;
    2383           0 :   uint32_t originalLength = aNodeArray.Length();
    2384           0 :   for (uint32_t i = 0; i < originalLength; i++) {
    2385           0 :     uint32_t idx = aStartOrEnd == StartOrEnd::start ?
    2386           0 :       (i - removedCount) : (originalLength - i - 1);
    2387           0 :     OwningNonNull<nsINode> endpoint = aNodeArray[idx];
    2388           0 :     if (endpoint == replaceNode ||
    2389           0 :         EditorUtils::IsDescendantOf(endpoint, replaceNode)) {
    2390           0 :       aNodeArray.RemoveElementAt(idx);
    2391           0 :       removedCount++;
    2392             :     }
    2393             :   }
    2394             : 
    2395             :   // Now replace the removed nodes with the structural parent
    2396           0 :   if (aStartOrEnd == StartOrEnd::end) {
    2397           0 :     aNodeArray.AppendElement(*replaceNode);
    2398             :   } else {
    2399           0 :     aNodeArray.InsertElementAt(0, *replaceNode);
    2400             :   }
    2401             : }
    2402             : 
    2403             : } // namespace mozilla

Generated by: LCOV version 1.13