editor/libeditor/html/nsHTMLDataTransfer.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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/. */
     7 #include <string.h>
     9 #include "mozilla/dom/DocumentFragment.h"
    10 #include "mozilla/ArrayUtils.h"
    11 #include "mozilla/Base64.h"
    12 #include "mozilla/BasicEvents.h"
    13 #include "mozilla/Preferences.h"
    14 #include "mozilla/dom/Selection.h"
    15 #include "nsAString.h"
    16 #include "nsAutoPtr.h"
    17 #include "nsCOMArray.h"
    18 #include "nsCOMPtr.h"
    19 #include "nsCRT.h"
    20 #include "nsCRTGlue.h"
    21 #include "nsComponentManagerUtils.h"
    22 #include "nsContentUtils.h"
    23 #include "nsDebug.h"
    24 #include "nsDependentSubstring.h"
    25 #include "nsEditProperty.h"
    26 #include "nsEditRules.h"
    27 #include "nsEditor.h"
    28 #include "nsEditorUtils.h"
    29 #include "nsError.h"
    30 #include "nsGkAtoms.h"
    31 #include "nsHTMLEditUtils.h"
    32 #include "nsHTMLEditor.h"
    33 #include "nsIClipboard.h"
    34 #include "nsIContent.h"
    35 #include "nsIContentFilter.h"
    36 #include "nsIDOMComment.h"
    37 #include "mozilla/dom/DOMStringList.h"
    38 #include "mozilla/dom/DataTransfer.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 "nsIDOMRange.h"
    53 #include "nsIDocument.h"
    54 #include "nsIEditor.h"
    55 #include "nsIEditorIMESupport.h"
    56 #include "nsIEditorMailSupport.h"
    57 #include "nsIFile.h"
    58 #include "nsIInputStream.h"
    59 #include "nsIMIMEService.h"
    60 #include "nsNameSpaceManager.h"
    61 #include "nsINode.h"
    62 #include "nsIParserUtils.h"
    63 #include "nsIPlaintextEditor.h"
    64 #include "nsISelection.h"
    65 #include "nsISupportsImpl.h"
    66 #include "nsISupportsPrimitives.h"
    67 #include "nsISupportsUtils.h"
    68 #include "nsITransferable.h"
    69 #include "nsIURI.h"
    70 #include "nsIVariant.h"
    71 #include "nsLinebreakConverter.h"
    72 #include "nsLiteralString.h"
    73 #include "nsNetUtil.h"
    74 #include "nsPlaintextEditor.h"
    75 #include "nsRange.h"
    76 #include "nsReadableUtils.h"
    77 #include "nsSelectionState.h"
    78 #include "nsServiceManagerUtils.h"
    79 #include "nsStreamUtils.h"
    80 #include "nsString.h"
    81 #include "nsStringFwd.h"
    82 #include "nsStringIterator.h"
    83 #include "nsSubstringTuple.h"
    84 #include "nsTextEditRules.h"
    85 #include "nsTextEditUtils.h"
    86 #include "nsTreeSanitizer.h"
    87 #include "nsWSRunObject.h"
    88 #include "nsXPCOM.h"
    89 #include "nscore.h"
    91 class nsIAtom;
    92 class nsILoadContext;
    93 class nsISupports;
    95 using namespace mozilla;
    96 using namespace mozilla::dom;
    98 #define kInsertCookie  "_moz_Insert Here_moz_"
   100 // some little helpers
   101 static bool FindIntegerAfterString(const char *aLeadingString, 
   102                                      nsCString &aCStr, int32_t &foundNumber);
   103 static nsresult RemoveFragComments(nsCString &theStr);
   104 static void RemoveBodyAndHead(nsIDOMNode *aNode);
   105 static nsresult FindTargetNode(nsIDOMNode *aStart, nsCOMPtr<nsIDOMNode> &aResult);
   107 static nsCOMPtr<nsIDOMNode> GetListParent(nsIDOMNode* aNode)
   108 {
   109   NS_ENSURE_TRUE(aNode, nullptr);
   110   nsCOMPtr<nsIDOMNode> parent, tmp;
   111   aNode->GetParentNode(getter_AddRefs(parent));
   112   while (parent)
   113   {
   114     if (nsHTMLEditUtils::IsList(parent)) {
   115       return parent;
   116     }
   117     parent->GetParentNode(getter_AddRefs(tmp));
   118     parent = tmp;
   119   }
   120   return nullptr;
   121 }
   123 static nsCOMPtr<nsIDOMNode> GetTableParent(nsIDOMNode* aNode)
   124 {
   125   NS_ENSURE_TRUE(aNode, nullptr);
   126   nsCOMPtr<nsIDOMNode> parent, tmp;
   127   aNode->GetParentNode(getter_AddRefs(parent));
   128   while (parent)
   129   {
   130     if (nsHTMLEditUtils::IsTable(parent)) {
   131       return parent;
   132     }
   133     parent->GetParentNode(getter_AddRefs(tmp));
   134     parent = tmp;
   135   }
   136   return nullptr;
   137 }
   140 NS_IMETHODIMP nsHTMLEditor::LoadHTML(const nsAString & aInputString)
   141 {
   142   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
   144   // force IME commit; set up rules sniffing and batching
   145   ForceCompositionEnd();
   146   nsAutoEditBatch beginBatching(this);
   147   nsAutoRules beginRulesSniffing(this, EditAction::loadHTML, nsIEditor::eNext);
   149   // Get selection
   150   nsRefPtr<Selection> selection = GetSelection();
   151   NS_ENSURE_STATE(selection);
   153   nsTextRulesInfo ruleInfo(EditAction::loadHTML);
   154   bool cancel, handled;
   155   // Protect the edit rules object from dying
   156   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
   157   nsresult rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   158   NS_ENSURE_SUCCESS(rv, rv);
   159   if (cancel) {
   160     return NS_OK; // rules canceled the operation
   161   }
   163   if (!handled)
   164   {
   165     // Delete Selection, but only if it isn't collapsed, see bug #106269
   166     if (!selection->Collapsed()) {
   167       rv = DeleteSelection(eNone, eStrip);
   168       NS_ENSURE_SUCCESS(rv, rv);
   169     }
   171     // Get the first range in the selection, for context:
   172     nsCOMPtr<nsIDOMRange> range;
   173     rv = selection->GetRangeAt(0, getter_AddRefs(range));
   174     NS_ENSURE_SUCCESS(rv, rv);
   175     NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
   177     // create fragment for pasted html
   178     nsCOMPtr<nsIDOMDocumentFragment> docfrag;
   179     {
   180       rv = range->CreateContextualFragment(aInputString, getter_AddRefs(docfrag));
   181       NS_ENSURE_SUCCESS(rv, rv);
   182     }
   183     // put the fragment into the document
   184     nsCOMPtr<nsIDOMNode> parent, junk;
   185     rv = range->GetStartContainer(getter_AddRefs(parent));
   186     NS_ENSURE_SUCCESS(rv, rv);
   187     NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
   188     int32_t childOffset;
   189     rv = range->GetStartOffset(&childOffset);
   190     NS_ENSURE_SUCCESS(rv, rv);
   192     nsCOMPtr<nsIDOMNode> nodeToInsert;
   193     docfrag->GetFirstChild(getter_AddRefs(nodeToInsert));
   194     while (nodeToInsert)
   195     {
   196       rv = InsertNode(nodeToInsert, parent, childOffset++);
   197       NS_ENSURE_SUCCESS(rv, rv);
   198       docfrag->GetFirstChild(getter_AddRefs(nodeToInsert));
   199     }
   200   }
   202   return mRules->DidDoAction(selection, &ruleInfo, rv);
   203 }
   206 NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsAString & aInString)
   207 {
   208   const nsAFlatString& empty = EmptyString();
   210   return InsertHTMLWithContext(aInString, empty, empty, empty,
   211                                nullptr,  nullptr, 0, true);
   212 }
   215 nsresult
   216 nsHTMLEditor::InsertHTMLWithContext(const nsAString & aInputString,
   217                                     const nsAString & aContextStr,
   218                                     const nsAString & aInfoStr,
   219                                     const nsAString & aFlavor,
   220                                     nsIDOMDocument *aSourceDoc,
   221                                     nsIDOMNode *aDestNode,
   222                                     int32_t aDestOffset,
   223                                     bool aDeleteSelection)
   224 {
   225   return DoInsertHTMLWithContext(aInputString, aContextStr, aInfoStr,
   226       aFlavor, aSourceDoc, aDestNode, aDestOffset, aDeleteSelection,
   227       /* trusted input */ true, /* clear style */ false);
   228 }
   230 nsresult
   231 nsHTMLEditor::DoInsertHTMLWithContext(const nsAString & aInputString,
   232                                       const nsAString & aContextStr,
   233                                       const nsAString & aInfoStr,
   234                                       const nsAString & aFlavor,
   235                                       nsIDOMDocument *aSourceDoc,
   236                                       nsIDOMNode *aDestNode,
   237                                       int32_t aDestOffset,
   238                                       bool aDeleteSelection,
   239                                       bool aTrustedInput,
   240                                       bool aClearStyle)
   241 {
   242   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
   244   // Prevent the edit rules object from dying
   245   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
   247   // force IME commit; set up rules sniffing and batching
   248   ForceCompositionEnd();
   249   nsAutoEditBatch beginBatching(this);
   250   nsAutoRules beginRulesSniffing(this, EditAction::htmlPaste, nsIEditor::eNext);
   252   // Get selection
   253   nsRefPtr<Selection> selection = GetSelection();
   254   NS_ENSURE_STATE(selection);
   256   // create a dom document fragment that represents the structure to paste
   257   nsCOMPtr<nsIDOMNode> fragmentAsNode, streamStartParent, streamEndParent;
   258   int32_t streamStartOffset = 0, streamEndOffset = 0;
   260   nsresult rv = CreateDOMFragmentFromPaste(aInputString, aContextStr, aInfoStr,
   261                                            address_of(fragmentAsNode),
   262                                            address_of(streamStartParent),
   263                                            address_of(streamEndParent),
   264                                            &streamStartOffset,
   265                                            &streamEndOffset,
   266                                            aTrustedInput);
   267   NS_ENSURE_SUCCESS(rv, rv);
   269   nsCOMPtr<nsIDOMNode> targetNode, tempNode;
   270   int32_t targetOffset=0;
   272   if (!aDestNode)
   273   {
   274     // if caller didn't provide the destination/target node,
   275     // fetch the paste insertion point from our selection
   276     rv = GetStartNodeAndOffset(selection, getter_AddRefs(targetNode), &targetOffset);
   277     NS_ENSURE_SUCCESS(rv, rv);
   278     if (!targetNode || !IsEditable(targetNode)) {
   279       return NS_ERROR_FAILURE;
   280     }
   281   }
   282   else
   283   {
   284     targetNode = aDestNode;
   285     targetOffset = aDestOffset;
   286   }
   288   bool doContinue = true;
   290   rv = DoContentFilterCallback(aFlavor, aSourceDoc, aDeleteSelection,
   291                                (nsIDOMNode **)address_of(fragmentAsNode),
   292                                (nsIDOMNode **)address_of(streamStartParent),
   293                                &streamStartOffset,
   294                                (nsIDOMNode **)address_of(streamEndParent),
   295                                &streamEndOffset,
   296                                (nsIDOMNode **)address_of(targetNode),
   297                                &targetOffset, &doContinue);
   299   NS_ENSURE_SUCCESS(rv, rv);
   300   NS_ENSURE_TRUE(doContinue, NS_OK);
   302   // if we have a destination / target node, we want to insert there
   303   // rather than in place of the selection
   304   // ignore aDeleteSelection here if no aDestNode since deletion will
   305   // also occur later; this block is intended to cover the various
   306   // scenarios where we are dropping in an editor (and may want to delete
   307   // the selection before collapsing the selection in the new destination)
   308   if (aDestNode)
   309   {
   310     if (aDeleteSelection)
   311     {
   312       // Use an auto tracker so that our drop point is correctly
   313       // positioned after the delete.
   314       nsAutoTrackDOMPoint tracker(mRangeUpdater, &targetNode, &targetOffset);
   315       rv = DeleteSelection(eNone, eStrip);
   316       NS_ENSURE_SUCCESS(rv, rv);
   317     }
   319     rv = selection->Collapse(targetNode, targetOffset);
   320     NS_ENSURE_SUCCESS(rv, rv);
   321   }
   323   // we need to recalculate various things based on potentially new offsets
   324   // this is work to be completed at a later date (probably by jfrancis)
   326   // make a list of what nodes in docFrag we need to move
   327   nsCOMArray<nsIDOMNode> nodeList;
   328   rv = CreateListOfNodesToPaste(fragmentAsNode, nodeList,
   329                                 streamStartParent, streamStartOffset,
   330                                 streamEndParent, streamEndOffset);
   331   NS_ENSURE_SUCCESS(rv, rv);
   333   if (nodeList.Count() == 0) {
   334     return NS_OK;
   335   }
   337   // Are there any table elements in the list?
   338   // node and offset for insertion
   339   nsCOMPtr<nsIDOMNode> parentNode;
   340   int32_t offsetOfNewNode;
   342   // check for table cell selection mode
   343   bool cellSelectionMode = false;
   344   nsCOMPtr<nsIDOMElement> cell;
   345   rv = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
   346   if (NS_SUCCEEDED(rv) && cell)
   347   {
   348     cellSelectionMode = true;
   349   }
   351   if (cellSelectionMode)
   352   {
   353     // do we have table content to paste?  If so, we want to delete
   354     // the selected table cells and replace with new table elements;
   355     // but if not we want to delete _contents_ of cells and replace
   356     // with non-table elements.  Use cellSelectionMode bool to 
   357     // indicate results.
   358     nsIDOMNode* firstNode = nodeList[0];
   359     if (!nsHTMLEditUtils::IsTableElement(firstNode))
   360       cellSelectionMode = false;
   361   }
   363   if (!cellSelectionMode)
   364   {
   365     rv = DeleteSelectionAndPrepareToCreateNode();
   366     NS_ENSURE_SUCCESS(rv, rv);
   368     if (aClearStyle) {
   369       // pasting does not inherit local inline styles
   370       nsCOMPtr<nsIDOMNode> tmpNode =
   371         do_QueryInterface(selection->GetAnchorNode());
   372       int32_t tmpOffset = static_cast<int32_t>(selection->AnchorOffset());
   373       rv = ClearStyle(address_of(tmpNode), &tmpOffset, nullptr, nullptr);
   374       NS_ENSURE_SUCCESS(rv, rv);
   375     }
   376   }
   377   else
   378   {
   379     // delete whole cells: we will replace with new table content
   380     { // Braces for artificial block to scope nsAutoSelectionReset.
   381       // Save current selection since DeleteTableCell perturbs it
   382       nsAutoSelectionReset selectionResetter(selection, this);
   383       rv = DeleteTableCell(1);
   384       NS_ENSURE_SUCCESS(rv, rv);
   385     }
   386     // collapse selection to beginning of deleted table content
   387     selection->CollapseToStart();
   388   }
   390   // give rules a chance to handle or cancel
   391   nsTextRulesInfo ruleInfo(EditAction::insertElement);
   392   bool cancel, handled;
   393   rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   394   NS_ENSURE_SUCCESS(rv, rv);
   395   if (cancel) {
   396     return NS_OK; // rules canceled the operation
   397   }
   399   if (!handled)
   400   {
   401     // The rules code (WillDoAction above) might have changed the selection.
   402     // refresh our memory...
   403     rv = GetStartNodeAndOffset(selection, getter_AddRefs(parentNode), &offsetOfNewNode);
   404     NS_ENSURE_SUCCESS(rv, rv);
   405     NS_ENSURE_TRUE(parentNode, NS_ERROR_FAILURE);
   407     // Adjust position based on the first node we are going to insert.
   408     NormalizeEOLInsertPosition(nodeList[0], address_of(parentNode), &offsetOfNewNode);
   410     // if there are any invisible br's after our insertion point, remove them.
   411     // this is because if there is a br at end of what we paste, it will make
   412     // the invisible br visible.
   413     nsWSRunObject wsObj(this, parentNode, offsetOfNewNode);
   414     if (nsTextEditUtils::IsBreak(wsObj.mEndReasonNode) && 
   415         !IsVisBreak(wsObj.mEndReasonNode) )
   416     {
   417       rv = DeleteNode(wsObj.mEndReasonNode);
   418       NS_ENSURE_SUCCESS(rv, rv);
   419     }
   421     // Remember if we are in a link.
   422     bool bStartedInLink = IsInLink(parentNode);
   424     // Are we in a text node? If so, split it.
   425     if (IsTextNode(parentNode))
   426     {
   427       nsCOMPtr<nsIDOMNode> temp;
   428       rv = SplitNodeDeep(parentNode, parentNode, offsetOfNewNode, &offsetOfNewNode);
   429       NS_ENSURE_SUCCESS(rv, rv);
   430       rv = parentNode->GetParentNode(getter_AddRefs(temp));
   431       NS_ENSURE_SUCCESS(rv, rv);
   432       parentNode = temp;
   433     }
   435     // build up list of parents of first node in list that are either
   436     // lists or tables.  First examine front of paste node list.
   437     nsCOMArray<nsIDOMNode> startListAndTableArray;
   438     rv = GetListAndTableParents(false, nodeList, startListAndTableArray);
   439     NS_ENSURE_SUCCESS(rv, rv);
   441     // remember number of lists and tables above us
   442     int32_t highWaterMark = -1;
   443     if (startListAndTableArray.Count() > 0)
   444     {
   445       rv = DiscoverPartialListsAndTables(nodeList, startListAndTableArray, &highWaterMark);
   446       NS_ENSURE_SUCCESS(rv, rv);
   447     }
   449     // if we have pieces of tables or lists to be inserted, let's force the paste 
   450     // to deal with table elements right away, so that it doesn't orphan some 
   451     // table or list contents outside the table or list.
   452     if (highWaterMark >= 0)
   453     {
   454       rv = ReplaceOrphanedStructure(false, nodeList, startListAndTableArray, highWaterMark);
   455       NS_ENSURE_SUCCESS(rv, rv);
   456     }
   458     // Now go through the same process again for the end of the paste node list.
   459     nsCOMArray<nsIDOMNode> endListAndTableArray;
   460     rv = GetListAndTableParents(true, nodeList, endListAndTableArray);
   461     NS_ENSURE_SUCCESS(rv, rv);
   462     highWaterMark = -1;
   464     // remember number of lists and tables above us
   465     if (endListAndTableArray.Count() > 0)
   466     {
   467       rv = DiscoverPartialListsAndTables(nodeList, endListAndTableArray, &highWaterMark);
   468       NS_ENSURE_SUCCESS(rv, rv);
   469     }
   471     // don't orphan partial list or table structure
   472     if (highWaterMark >= 0)
   473     {
   474       rv = ReplaceOrphanedStructure(true, nodeList, endListAndTableArray, highWaterMark);
   475       NS_ENSURE_SUCCESS(rv, rv);
   476     }
   478     // Loop over the node list and paste the nodes:
   479     nsCOMPtr<nsIDOMNode> parentBlock, lastInsertNode, insertedContextParent;
   480     int32_t listCount = nodeList.Count();
   481     int32_t j;
   482     if (IsBlockNode(parentNode))
   483       parentBlock = parentNode;
   484     else
   485       parentBlock = GetBlockNodeParent(parentNode);
   487     for (j=0; j<listCount; j++)
   488     {
   489       bool bDidInsert = false;
   490       nsCOMPtr<nsIDOMNode> curNode = nodeList[j];
   492       NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE);
   493       NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE);
   494       NS_ENSURE_TRUE(!nsTextEditUtils::IsBody(curNode), NS_ERROR_FAILURE);
   496       if (insertedContextParent)
   497       {
   498         // if we had to insert something higher up in the paste hierarchy, we want to 
   499         // skip any further paste nodes that descend from that.  Else we will paste twice.
   500         if (nsEditorUtils::IsDescendantOf(curNode, insertedContextParent))
   501           continue;
   502       }
   504       // give the user a hand on table element insertion.  if they have
   505       // a table or table row on the clipboard, and are trying to insert
   506       // into a table or table row, insert the appropriate children instead.
   507       if (  (nsHTMLEditUtils::IsTableRow(curNode) && nsHTMLEditUtils::IsTableRow(parentNode))
   508          && (nsHTMLEditUtils::IsTable(curNode)    || nsHTMLEditUtils::IsTable(parentNode)) )
   509       {
   510         nsCOMPtr<nsIDOMNode> child;
   511         curNode->GetFirstChild(getter_AddRefs(child));
   512         while (child)
   513         {
   514           rv = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, true);
   515           if (NS_FAILED(rv))
   516             break;
   518           bDidInsert = true;
   519           lastInsertNode = child;
   520           offsetOfNewNode++;
   522           curNode->GetFirstChild(getter_AddRefs(child));
   523         }
   524       }
   525       // give the user a hand on list insertion.  if they have
   526       // a list on the clipboard, and are trying to insert
   527       // into a list or list item, insert the appropriate children instead,
   528       // ie, merge the lists instead of pasting in a sublist.
   529       else if (nsHTMLEditUtils::IsList(curNode) && 
   530               (nsHTMLEditUtils::IsList(parentNode)  || nsHTMLEditUtils::IsListItem(parentNode)) )
   531       {
   532         nsCOMPtr<nsIDOMNode> child, tmp;
   533         curNode->GetFirstChild(getter_AddRefs(child));
   534         while (child)
   535         {
   536           if (nsHTMLEditUtils::IsListItem(child) || nsHTMLEditUtils::IsList(child))
   537           {
   538             // Check if we are pasting into empty list item. If so
   539             // delete it and paste into parent list instead.
   540             if (nsHTMLEditUtils::IsListItem(parentNode))
   541             {
   542               bool isEmpty;
   543               rv = IsEmptyNode(parentNode, &isEmpty, true);
   544               if (NS_SUCCEEDED(rv) && isEmpty)
   545               {
   546                 int32_t newOffset;
   547                 nsCOMPtr<nsIDOMNode> listNode = GetNodeLocation(parentNode, &newOffset);
   548                 if (listNode)
   549                 {
   550                   DeleteNode(parentNode);
   551                   parentNode = listNode;
   552                   offsetOfNewNode = newOffset;
   553                 }
   554               }
   555             }
   556             rv = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, true);
   557             if (NS_FAILED(rv))
   558               break;
   560             bDidInsert = true;
   561             lastInsertNode = child;
   562             offsetOfNewNode++;
   563           }
   564           else
   565           {
   566             curNode->RemoveChild(child, getter_AddRefs(tmp));
   567           }
   568           curNode->GetFirstChild(getter_AddRefs(child));
   569         }
   571       }
   572       // Check for pre's going into pre's.
   573       else if (nsHTMLEditUtils::IsPre(parentBlock) && nsHTMLEditUtils::IsPre(curNode))
   574       {
   575         nsCOMPtr<nsIDOMNode> child, tmp;
   576         curNode->GetFirstChild(getter_AddRefs(child));
   577         while (child)
   578         {
   579           rv = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, true);
   580           if (NS_FAILED(rv))
   581             break;
   583           bDidInsert = true;
   584           lastInsertNode = child;
   585           offsetOfNewNode++;
   587           curNode->GetFirstChild(getter_AddRefs(child));
   588         }
   589       }
   591       if (!bDidInsert || NS_FAILED(rv))
   592       {
   593         // try to insert
   594         rv = InsertNodeAtPoint(curNode, address_of(parentNode), &offsetOfNewNode, true);
   595         if (NS_SUCCEEDED(rv))
   596         {
   597           bDidInsert = true;
   598           lastInsertNode = curNode;
   599         }
   601         // Assume failure means no legal parent in the document hierarchy,
   602         // try again with the parent of curNode in the paste hierarchy.
   603         nsCOMPtr<nsIDOMNode> parent;
   604         while (NS_FAILED(rv) && curNode)
   605         {
   606           curNode->GetParentNode(getter_AddRefs(parent));
   607           if (parent && !nsTextEditUtils::IsBody(parent))
   608           {
   609             rv = InsertNodeAtPoint(parent, address_of(parentNode), &offsetOfNewNode, true);
   610             if (NS_SUCCEEDED(rv))
   611             {
   612               bDidInsert = true;
   613               insertedContextParent = parent;
   614               lastInsertNode = GetChildAt(parentNode, offsetOfNewNode);
   615             }
   616           }
   617           curNode = parent;
   618         }
   619       }
   620       if (lastInsertNode)
   621       {
   622         parentNode = GetNodeLocation(lastInsertNode, &offsetOfNewNode);
   623         offsetOfNewNode++;
   624       }
   625     }
   627     // Now collapse the selection to the end of what we just inserted:
   628     if (lastInsertNode) 
   629     {
   630       // set selection to the end of what we just pasted.
   631       nsCOMPtr<nsIDOMNode> selNode, tmp, visNode, highTable;
   632       int32_t selOffset;
   634       // but don't cross tables
   635       if (!nsHTMLEditUtils::IsTable(lastInsertNode))
   636       {
   637         rv = GetLastEditableLeaf(lastInsertNode, address_of(selNode));
   638         NS_ENSURE_SUCCESS(rv, rv);
   639         tmp = selNode;
   640         while (tmp && (tmp != lastInsertNode))
   641         {
   642           if (nsHTMLEditUtils::IsTable(tmp))
   643             highTable = tmp;
   644           nsCOMPtr<nsIDOMNode> parent = tmp;
   645           tmp->GetParentNode(getter_AddRefs(parent));
   646           tmp = parent;
   647         }
   648         if (highTable)
   649           selNode = highTable;
   650       }
   651       if (!selNode)
   652         selNode = lastInsertNode;
   653       if (IsTextNode(selNode) || (IsContainer(selNode) && !nsHTMLEditUtils::IsTable(selNode)))
   654       {
   655         rv = GetLengthOfDOMNode(selNode, (uint32_t&)selOffset);
   656         NS_ENSURE_SUCCESS(rv, rv);
   657       }
   658       else // we need to find a container for selection.  Look up.
   659       {
   660         tmp = selNode;
   661         selNode = GetNodeLocation(tmp, &selOffset);
   662         ++selOffset;  // want to be *after* last leaf node in paste
   663       }
   665       // make sure we don't end up with selection collapsed after an invisible break node
   666       nsWSRunObject wsRunObj(this, selNode, selOffset);
   667       int32_t outVisOffset=0;
   668       WSType visType;
   669       wsRunObj.PriorVisibleNode(selNode, selOffset, address_of(visNode),
   670                                 &outVisOffset, &visType);
   671       if (visType == WSType::br) {
   672         // we are after a break.  Is it visible?  Despite the name, 
   673         // PriorVisibleNode does not make that determination for breaks.
   674         // It also may not return the break in visNode.  We have to pull it
   675         // out of the nsWSRunObject's state.
   676         if (!IsVisBreak(wsRunObj.mStartReasonNode))
   677         {
   678           // don't leave selection past an invisible break;
   679           // reset {selNode,selOffset} to point before break
   680           selNode = GetNodeLocation(wsRunObj.mStartReasonNode, &selOffset);
   681           // we want to be inside any inline style prior to break
   682           nsWSRunObject wsRunObj(this, selNode, selOffset);
   683           wsRunObj.PriorVisibleNode(selNode, selOffset, address_of(visNode),
   684                                     &outVisOffset, &visType);
   685           if (visType == WSType::text || visType == WSType::normalWS) {
   686             selNode = visNode;
   687             selOffset = outVisOffset;  // PriorVisibleNode already set offset to _after_ the text or ws
   688           } else if (visType == WSType::special) {
   689             // prior visible thing is an image or some other non-text thingy.  
   690             // We want to be right after it.
   691             selNode = GetNodeLocation(wsRunObj.mStartReasonNode, &selOffset);
   692             ++selOffset;
   693           }
   694         }
   695       }
   696       selection->Collapse(selNode, selOffset);
   698       // if we just pasted a link, discontinue link style
   699       nsCOMPtr<nsIDOMNode> link;
   700       if (!bStartedInLink && IsInLink(selNode, address_of(link)))
   701       {
   702         // so, if we just pasted a link, I split it.  Why do that instead of just
   703         // nudging selection point beyond it?  Because it might have ended in a BR
   704         // that is not visible.  If so, the code above just placed selection
   705         // inside that.  So I split it instead.
   706         nsCOMPtr<nsIDOMNode> leftLink;
   707         int32_t linkOffset;
   708         rv = SplitNodeDeep(link, selNode, selOffset, &linkOffset, true, address_of(leftLink));
   709         NS_ENSURE_SUCCESS(rv, rv);
   710         selNode = GetNodeLocation(leftLink, &selOffset);
   711         selection->Collapse(selNode, selOffset+1);
   712       }
   713     }
   714   }
   716   return mRules->DidDoAction(selection, &ruleInfo, rv);
   717 }
   719 nsresult
   720 nsHTMLEditor::AddInsertionListener(nsIContentFilter *aListener)
   721 {
   722   NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
   724   // don't let a listener be added more than once
   725   if (mContentFilters.IndexOfObject(aListener) == -1)
   726   {
   727     NS_ENSURE_TRUE(mContentFilters.AppendObject(aListener), NS_ERROR_FAILURE);
   728   }
   730   return NS_OK;
   731 }
   733 nsresult
   734 nsHTMLEditor::RemoveInsertionListener(nsIContentFilter *aListener)
   735 {
   736   NS_ENSURE_TRUE(aListener, NS_ERROR_FAILURE);
   738   NS_ENSURE_TRUE(mContentFilters.RemoveObject(aListener), NS_ERROR_FAILURE);
   740   return NS_OK;
   741 }
   743 nsresult
   744 nsHTMLEditor::DoContentFilterCallback(const nsAString &aFlavor, 
   745                                       nsIDOMDocument *sourceDoc,
   746                                       bool aWillDeleteSelection,
   747                                       nsIDOMNode **aFragmentAsNode, 
   748                                       nsIDOMNode **aFragStartNode, 
   749                                       int32_t *aFragStartOffset,
   750                                       nsIDOMNode **aFragEndNode, 
   751                                       int32_t *aFragEndOffset,
   752                                       nsIDOMNode **aTargetNode,
   753                                       int32_t *aTargetOffset,
   754                                       bool *aDoContinue)
   755 {
   756   *aDoContinue = true;
   758   int32_t i;
   759   nsIContentFilter *listener;
   760   for (i=0; i < mContentFilters.Count() && *aDoContinue; i++)
   761   {
   762     listener = (nsIContentFilter *)mContentFilters[i];
   763     if (listener)
   764       listener->NotifyOfInsertion(aFlavor, nullptr, sourceDoc,
   765                                   aWillDeleteSelection, aFragmentAsNode,
   766                                   aFragStartNode, aFragStartOffset, 
   767                                   aFragEndNode, aFragEndOffset,
   768                                   aTargetNode, aTargetOffset, aDoContinue);
   769   }
   771   return NS_OK;
   772 }
   774 bool
   775 nsHTMLEditor::IsInLink(nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *outLink)
   776 {
   777   NS_ENSURE_TRUE(aNode, false);
   778   if (outLink)
   779     *outLink = nullptr;
   780   nsCOMPtr<nsIDOMNode> tmp, node = aNode;
   781   while (node)
   782   {
   783     if (nsHTMLEditUtils::IsLink(node)) 
   784     {
   785       if (outLink)
   786         *outLink = node;
   787       return true;
   788     }
   789     tmp = node;
   790     tmp->GetParentNode(getter_AddRefs(node));
   791   }
   792   return false;
   793 }
   796 nsresult
   797 nsHTMLEditor::StripFormattingNodes(nsIDOMNode *aNode, bool aListOnly)
   798 {
   799   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
   801   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
   802   if (content->TextIsOnlyWhitespace())
   803   {
   804     nsCOMPtr<nsIDOMNode> parent, ignored;
   805     aNode->GetParentNode(getter_AddRefs(parent));
   806     if (parent)
   807     {
   808       if (!aListOnly || nsHTMLEditUtils::IsList(parent)) {
   809         return parent->RemoveChild(aNode, getter_AddRefs(ignored));
   810       }
   811       return NS_OK;
   812     }
   813   }
   815   if (!nsHTMLEditUtils::IsPre(aNode))
   816   {
   817     nsCOMPtr<nsIDOMNode> child;
   818     aNode->GetLastChild(getter_AddRefs(child));
   820     while (child)
   821     {
   822       nsCOMPtr<nsIDOMNode> tmp;
   823       child->GetPreviousSibling(getter_AddRefs(tmp));
   824       nsresult rv = StripFormattingNodes(child, aListOnly);
   825       NS_ENSURE_SUCCESS(rv, rv);
   826       child = tmp;
   827     }
   828   }
   829   return NS_OK;
   830 }
   832 NS_IMETHODIMP nsHTMLEditor::PrepareTransferable(nsITransferable **transferable)
   833 {
   834   return NS_OK;
   835 }
   837 NS_IMETHODIMP nsHTMLEditor::PrepareHTMLTransferable(nsITransferable **aTransferable, 
   838                                                     bool aHavePrivFlavor)
   839 {
   840   // Create generic Transferable for getting the data
   841   nsresult rv = CallCreateInstance("@mozilla.org/widget/transferable;1", aTransferable);
   842   NS_ENSURE_SUCCESS(rv, rv);
   844   // Get the nsITransferable interface for getting the data from the clipboard
   845   if (aTransferable)
   846   {
   847     nsCOMPtr<nsIDocument> destdoc = GetDocument();
   848     nsILoadContext* loadContext = destdoc ? destdoc->GetLoadContext() : nullptr;
   849     (*aTransferable)->Init(loadContext);
   851     // Create the desired DataFlavor for the type of data
   852     // we want to get out of the transferable
   853     // This should only happen in html editors, not plaintext
   854     if (!IsPlaintextEditor())
   855     {
   856       if (!aHavePrivFlavor) 
   857       {
   858         (*aTransferable)->AddDataFlavor(kNativeHTMLMime);
   859       }
   860       (*aTransferable)->AddDataFlavor(kHTMLMime);
   861       (*aTransferable)->AddDataFlavor(kFileMime);
   863       switch (Preferences::GetInt("clipboard.paste_image_type", 1))
   864       {
   865         case 0:  // prefer JPEG over PNG over GIF encoding
   866           (*aTransferable)->AddDataFlavor(kJPEGImageMime);
   867           (*aTransferable)->AddDataFlavor(kJPGImageMime);
   868           (*aTransferable)->AddDataFlavor(kPNGImageMime);
   869           (*aTransferable)->AddDataFlavor(kGIFImageMime);
   870           break;
   871         case 1:  // prefer PNG over JPEG over GIF encoding (default)
   872         default:
   873           (*aTransferable)->AddDataFlavor(kPNGImageMime);
   874           (*aTransferable)->AddDataFlavor(kJPEGImageMime);
   875           (*aTransferable)->AddDataFlavor(kJPGImageMime);
   876           (*aTransferable)->AddDataFlavor(kGIFImageMime);
   877           break;
   878         case 2:  // prefer GIF over JPEG over PNG encoding
   879           (*aTransferable)->AddDataFlavor(kGIFImageMime);
   880           (*aTransferable)->AddDataFlavor(kJPEGImageMime);
   881           (*aTransferable)->AddDataFlavor(kJPGImageMime);
   882           (*aTransferable)->AddDataFlavor(kPNGImageMime);
   883           break;
   884       }
   885     }
   886     (*aTransferable)->AddDataFlavor(kUnicodeMime);
   887     (*aTransferable)->AddDataFlavor(kMozTextInternal);
   888   }
   890   return NS_OK;
   891 }
   893 bool
   894 FindIntegerAfterString(const char *aLeadingString, 
   895                        nsCString &aCStr, int32_t &foundNumber)
   896 {
   897   // first obtain offsets from cfhtml str
   898   int32_t numFront = aCStr.Find(aLeadingString);
   899   if (numFront == -1)
   900     return false;
   901   numFront += strlen(aLeadingString);
   903   int32_t numBack = aCStr.FindCharInSet(CRLF, numFront);
   904   if (numBack == -1)
   905     return false;
   907   nsAutoCString numStr(Substring(aCStr, numFront, numBack-numFront));
   908   nsresult errorCode;
   909   foundNumber = numStr.ToInteger(&errorCode);
   910   return true;
   911 }
   913 nsresult
   914 RemoveFragComments(nsCString & aStr)
   915 {
   916   // remove the StartFragment/EndFragment comments from the str, if present
   917   int32_t startCommentIndx = aStr.Find("<!--StartFragment");
   918   if (startCommentIndx >= 0)
   919   {
   920     int32_t startCommentEnd = aStr.Find("-->", false, startCommentIndx);
   921     if (startCommentEnd > startCommentIndx)
   922       aStr.Cut(startCommentIndx, (startCommentEnd+3)-startCommentIndx);
   923   }
   924   int32_t endCommentIndx = aStr.Find("<!--EndFragment");
   925   if (endCommentIndx >= 0)
   926   {
   927     int32_t endCommentEnd = aStr.Find("-->", false, endCommentIndx);
   928     if (endCommentEnd > endCommentIndx)
   929       aStr.Cut(endCommentIndx, (endCommentEnd+3)-endCommentIndx);
   930   }
   931   return NS_OK;
   932 }
   934 nsresult
   935 nsHTMLEditor::ParseCFHTML(nsCString & aCfhtml, char16_t **aStuffToPaste, char16_t **aCfcontext)
   936 {
   937   // First obtain offsets from cfhtml str.
   938   int32_t startHTML, endHTML, startFragment, endFragment;
   939   if (!FindIntegerAfterString("StartHTML:", aCfhtml, startHTML) || 
   940       startHTML < -1)
   941     return NS_ERROR_FAILURE;
   942   if (!FindIntegerAfterString("EndHTML:", aCfhtml, endHTML) || 
   943       endHTML < -1)
   944     return NS_ERROR_FAILURE;
   945   if (!FindIntegerAfterString("StartFragment:", aCfhtml, startFragment) || 
   946       startFragment < 0)
   947     return NS_ERROR_FAILURE;
   948   if (!FindIntegerAfterString("EndFragment:", aCfhtml, endFragment) || 
   949       startFragment < 0)
   950     return NS_ERROR_FAILURE;
   952   // The StartHTML and EndHTML markers are allowed to be -1 to include everything.
   953   //   See Reference: MSDN doc entitled "HTML Clipboard Format"
   954   //   http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx#unknown_854
   955   if (startHTML == -1) {
   956     startHTML = aCfhtml.Find("<!--StartFragment-->");
   957     if (startHTML == -1)
   958       return NS_OK;
   959   }
   960   if (endHTML == -1) {
   961     const char endFragmentMarker[] = "<!--EndFragment-->";
   962     endHTML = aCfhtml.Find(endFragmentMarker);
   963     if (endHTML == -1)
   964       return NS_OK;
   965     endHTML += ArrayLength(endFragmentMarker) - 1;
   966   }
   968   // create context string
   969   nsAutoCString contextUTF8(Substring(aCfhtml, startHTML, startFragment - startHTML) +
   970                             NS_LITERAL_CSTRING("<!--" kInsertCookie "-->") +
   971                             Substring(aCfhtml, endFragment, endHTML - endFragment));
   973   // validate startFragment
   974   // make sure it's not in the middle of a HTML tag
   975   // see bug #228879 for more details
   976   int32_t curPos = startFragment;
   977   while (curPos > startHTML)
   978   {
   979       if (aCfhtml[curPos] == '>')
   980       {
   981           // working backwards, the first thing we see is the end of a tag
   982           // so StartFragment is good, so do nothing.
   983           break;
   984       }
   985       else if (aCfhtml[curPos] == '<') 
   986       {
   987           // if we are at the start, then we want to see the '<'
   988           if (curPos != startFragment) 
   989           {
   990               // working backwards, the first thing we see is the start of a tag
   991               // so StartFragment is bad, so we need to update it.
   992               NS_ERROR("StartFragment byte count in the clipboard looks bad, see bug #228879");
   993               startFragment = curPos - 1;
   994           }
   995           break;
   996       }
   997       else 
   998       {
   999           curPos--;
  1003   // create fragment string
  1004   nsAutoCString fragmentUTF8(Substring(aCfhtml, startFragment, endFragment-startFragment));
  1006   // remove the StartFragment/EndFragment comments from the fragment, if present
  1007   RemoveFragComments(fragmentUTF8);
  1009   // remove the StartFragment/EndFragment comments from the context, if present
  1010   RemoveFragComments(contextUTF8);
  1012   // convert both strings to usc2
  1013   const nsAFlatString& fragUcs2Str = NS_ConvertUTF8toUTF16(fragmentUTF8);
  1014   const nsAFlatString& cntxtUcs2Str = NS_ConvertUTF8toUTF16(contextUTF8);
  1016   // translate platform linebreaks for fragment
  1017   int32_t oldLengthInChars = fragUcs2Str.Length() + 1;  // +1 to include null terminator
  1018   int32_t newLengthInChars = 0;
  1019   *aStuffToPaste = nsLinebreakConverter::ConvertUnicharLineBreaks(fragUcs2Str.get(),
  1020                                                            nsLinebreakConverter::eLinebreakAny, 
  1021                                                            nsLinebreakConverter::eLinebreakContent, 
  1022                                                            oldLengthInChars, &newLengthInChars);
  1023   NS_ENSURE_TRUE(*aStuffToPaste, NS_ERROR_FAILURE);
  1025   // translate platform linebreaks for context
  1026   oldLengthInChars = cntxtUcs2Str.Length() + 1;  // +1 to include null terminator
  1027   newLengthInChars = 0;
  1028   *aCfcontext = nsLinebreakConverter::ConvertUnicharLineBreaks(cntxtUcs2Str.get(),
  1029                                                            nsLinebreakConverter::eLinebreakAny, 
  1030                                                            nsLinebreakConverter::eLinebreakContent, 
  1031                                                            oldLengthInChars, &newLengthInChars);
  1032   // it's ok for context to be empty.  frag might be whole doc and contain all its context.
  1034   // we're done!
  1035   return NS_OK;
  1038 nsresult nsHTMLEditor::InsertObject(const char* aType, nsISupports* aObject, bool aIsSafe,
  1039                                     nsIDOMDocument *aSourceDoc,
  1040                                     nsIDOMNode *aDestinationNode,
  1041                                     int32_t aDestOffset,
  1042                                     bool aDoDeleteSelection)
  1044   nsresult rv;
  1046   const char* type = aType;
  1048   // Check to see if we can insert an image file
  1049   bool insertAsImage = false;
  1050   nsCOMPtr<nsIURI> fileURI;
  1051   if (0 == nsCRT::strcmp(type, kFileMime))
  1053     nsCOMPtr<nsIFile> fileObj = do_QueryInterface(aObject);
  1054     if (fileObj)
  1056       rv = NS_NewFileURI(getter_AddRefs(fileURI), fileObj);
  1057       NS_ENSURE_SUCCESS(rv, rv);
  1059       nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1");
  1060       NS_ENSURE_TRUE(mime, NS_ERROR_FAILURE);
  1061       nsAutoCString contentType;
  1062       rv = mime->GetTypeFromFile(fileObj, contentType);
  1063       NS_ENSURE_SUCCESS(rv, rv);
  1065       // Accept any image type fed to us
  1066       if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("image/"))) {
  1067         insertAsImage = true;
  1068         type = contentType.get();
  1073   if (0 == nsCRT::strcmp(type, kJPEGImageMime) ||
  1074       0 == nsCRT::strcmp(type, kJPGImageMime) ||
  1075       0 == nsCRT::strcmp(type, kPNGImageMime) ||
  1076       0 == nsCRT::strcmp(type, kGIFImageMime) ||
  1077       insertAsImage)
  1079     nsCOMPtr<nsIInputStream> imageStream;
  1080     if (insertAsImage) {
  1081       NS_ASSERTION(fileURI, "The file URI should be retrieved earlier");
  1082       rv = NS_OpenURI(getter_AddRefs(imageStream), fileURI);
  1083       NS_ENSURE_SUCCESS(rv, rv);
  1084     } else {
  1085       imageStream = do_QueryInterface(aObject);
  1086       NS_ENSURE_TRUE(imageStream, NS_ERROR_FAILURE);
  1089     nsCString imageData;
  1090     rv = NS_ConsumeStream(imageStream, UINT32_MAX, imageData);
  1091     NS_ENSURE_SUCCESS(rv, rv);
  1093     rv = imageStream->Close();
  1094     NS_ENSURE_SUCCESS(rv, rv);
  1096     nsAutoCString data64;
  1097     rv = Base64Encode(imageData, data64);
  1098     NS_ENSURE_SUCCESS(rv, rv);
  1100     nsAutoString stuffToPaste;
  1101     stuffToPaste.AssignLiteral("<IMG src=\"data:");
  1102     AppendUTF8toUTF16(type, stuffToPaste);
  1103     stuffToPaste.AppendLiteral(";base64,");
  1104     AppendUTF8toUTF16(data64, stuffToPaste);
  1105     stuffToPaste.AppendLiteral("\" alt=\"\" >");
  1106     nsAutoEditBatch beginBatching(this);
  1107     rv = DoInsertHTMLWithContext(stuffToPaste, EmptyString(), EmptyString(), 
  1108                                  NS_LITERAL_STRING(kFileMime),
  1109                                  aSourceDoc,
  1110                                  aDestinationNode, aDestOffset,
  1111                                  aDoDeleteSelection,
  1112                                  aIsSafe);
  1115   return NS_OK;
  1118 NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable, 
  1119                                                    nsIDOMDocument *aSourceDoc,
  1120                                                    const nsAString & aContextStr,
  1121                                                    const nsAString & aInfoStr,
  1122                                                    nsIDOMNode *aDestinationNode,
  1123                                                    int32_t aDestOffset,
  1124                                                    bool aDoDeleteSelection)
  1126   nsresult rv = NS_OK;
  1127   nsXPIDLCString bestFlavor;
  1128   nsCOMPtr<nsISupports> genericDataObj;
  1129   uint32_t len = 0;
  1130   if (NS_SUCCEEDED(transferable->GetAnyTransferData(getter_Copies(bestFlavor), getter_AddRefs(genericDataObj), &len)))
  1132     nsAutoTxnsConserveSelection dontSpazMySelection(this);
  1133     nsAutoString flavor;
  1134     flavor.AssignWithConversion(bestFlavor);
  1135     nsAutoString stuffToPaste;
  1136 #ifdef DEBUG_clipboard
  1137     printf("Got flavor [%s]\n", bestFlavor.get());
  1138 #endif
  1140     bool isSafe = IsSafeToInsertData(aSourceDoc);
  1142     if (0 == nsCRT::strcmp(bestFlavor, kFileMime) ||
  1143         0 == nsCRT::strcmp(bestFlavor, kJPEGImageMime) ||
  1144         0 == nsCRT::strcmp(bestFlavor, kJPGImageMime) ||
  1145         0 == nsCRT::strcmp(bestFlavor, kPNGImageMime) ||
  1146         0 == nsCRT::strcmp(bestFlavor, kGIFImageMime)) {
  1147       rv = InsertObject(bestFlavor, genericDataObj, isSafe,
  1148                         aSourceDoc, aDestinationNode, aDestOffset, aDoDeleteSelection);
  1150     else if (0 == nsCRT::strcmp(bestFlavor, kNativeHTMLMime))
  1152       // note cf_html uses utf8, hence use length = len, not len/2 as in flavors below
  1153       nsCOMPtr<nsISupportsCString> textDataObj = do_QueryInterface(genericDataObj);
  1154       if (textDataObj && len > 0)
  1156         nsAutoCString cfhtml;
  1157         textDataObj->GetData(cfhtml);
  1158         NS_ASSERTION(cfhtml.Length() <= (len), "Invalid length!");
  1159         nsXPIDLString cfcontext, cffragment, cfselection; // cfselection left emtpy for now
  1161         rv = ParseCFHTML(cfhtml, getter_Copies(cffragment), getter_Copies(cfcontext));
  1162         if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty())
  1164           nsAutoEditBatch beginBatching(this);
  1165           rv = DoInsertHTMLWithContext(cffragment,
  1166                                        cfcontext, cfselection, flavor,
  1167                                        aSourceDoc,
  1168                                        aDestinationNode, aDestOffset,
  1169                                        aDoDeleteSelection,
  1170                                        isSafe);
  1171         } else {
  1172           // In some platforms (like Linux), the clipboard might return data
  1173           // requested for unknown flavors (for example:
  1174           // application/x-moz-nativehtml).  In this case, treat the data
  1175           // to be pasted as mere HTML to get the best chance of pasting it
  1176           // correctly.
  1177           bestFlavor.AssignLiteral(kHTMLMime);
  1178           // Fall through the next case
  1182     if (0 == nsCRT::strcmp(bestFlavor, kHTMLMime) ||
  1183         0 == nsCRT::strcmp(bestFlavor, kUnicodeMime) ||
  1184         0 == nsCRT::strcmp(bestFlavor, kMozTextInternal)) {
  1185       nsCOMPtr<nsISupportsString> textDataObj = do_QueryInterface(genericDataObj);
  1186       if (textDataObj && len > 0) {
  1187         nsAutoString text;
  1188         textDataObj->GetData(text);
  1189         NS_ASSERTION(text.Length() <= (len/2), "Invalid length!");
  1190         stuffToPaste.Assign(text.get(), len / 2);
  1191       } else {
  1192         nsCOMPtr<nsISupportsCString> textDataObj(do_QueryInterface(genericDataObj));
  1193         if (textDataObj && len > 0) {
  1194           nsAutoCString text;
  1195           textDataObj->GetData(text);
  1196           NS_ASSERTION(text.Length() <= len, "Invalid length!");
  1197           stuffToPaste.Assign(NS_ConvertUTF8toUTF16(Substring(text, 0, len)));
  1201       if (!stuffToPaste.IsEmpty()) {
  1202         nsAutoEditBatch beginBatching(this);
  1203         if (0 == nsCRT::strcmp(bestFlavor, kHTMLMime)) {
  1204           rv = DoInsertHTMLWithContext(stuffToPaste,
  1205                                        aContextStr, aInfoStr, flavor,
  1206                                        aSourceDoc,
  1207                                        aDestinationNode, aDestOffset,
  1208                                        aDoDeleteSelection,
  1209                                        isSafe);
  1210         } else {
  1211           rv = InsertTextAt(stuffToPaste, aDestinationNode, aDestOffset, aDoDeleteSelection);
  1217   // Try to scroll the selection into view if the paste succeeded
  1218   if (NS_SUCCEEDED(rv))
  1219     ScrollSelectionIntoView(false);
  1221   return rv;
  1224 static void
  1225 GetStringFromDataTransfer(nsIDOMDataTransfer *aDataTransfer, const nsAString& aType,
  1226                           int32_t aIndex, nsAString& aOutputString)
  1228   nsCOMPtr<nsIVariant> variant;
  1229   aDataTransfer->MozGetDataAt(aType, aIndex, getter_AddRefs(variant));
  1230   if (variant)
  1231     variant->GetAsAString(aOutputString);
  1234 nsresult nsHTMLEditor::InsertFromDataTransfer(DataTransfer *aDataTransfer,
  1235                                               int32_t aIndex,
  1236                                               nsIDOMDocument *aSourceDoc,
  1237                                               nsIDOMNode *aDestinationNode,
  1238                                               int32_t aDestOffset,
  1239                                               bool aDoDeleteSelection)
  1241   ErrorResult rv;
  1242   nsRefPtr<DOMStringList> types = aDataTransfer->MozTypesAt(aIndex, rv);
  1243   if (rv.Failed()) {
  1244     return rv.ErrorCode();
  1247   bool hasPrivateHTMLFlavor = types->Contains(NS_LITERAL_STRING(kHTMLContext));
  1249   bool isText = IsPlaintextEditor();
  1250   bool isSafe = IsSafeToInsertData(aSourceDoc);
  1252   uint32_t length = types->Length();
  1253   for (uint32_t t = 0; t < length; t++) {
  1254     nsAutoString type;
  1255     types->Item(t, type);
  1257     if (!isText) {
  1258       if (type.EqualsLiteral(kFileMime) ||
  1259           type.EqualsLiteral(kJPEGImageMime) ||
  1260           type.EqualsLiteral(kJPGImageMime) ||
  1261           type.EqualsLiteral(kPNGImageMime) ||
  1262           type.EqualsLiteral(kGIFImageMime)) {
  1263         nsCOMPtr<nsIVariant> variant;
  1264         aDataTransfer->MozGetDataAt(type, aIndex, getter_AddRefs(variant));
  1265         if (variant) {
  1266           nsCOMPtr<nsISupports> object;
  1267           variant->GetAsISupports(getter_AddRefs(object));
  1268           return InsertObject(NS_ConvertUTF16toUTF8(type).get(), object, isSafe,
  1269                               aSourceDoc, aDestinationNode, aDestOffset, aDoDeleteSelection);
  1272       else if (!hasPrivateHTMLFlavor && type.EqualsLiteral(kNativeHTMLMime)) {
  1273         nsAutoString text;
  1274         GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kNativeHTMLMime), aIndex, text);
  1275         NS_ConvertUTF16toUTF8 cfhtml(text);
  1277         nsXPIDLString cfcontext, cffragment, cfselection; // cfselection left emtpy for now
  1279         nsresult rv = ParseCFHTML(cfhtml, getter_Copies(cffragment), getter_Copies(cfcontext));
  1280         if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty())
  1282           nsAutoEditBatch beginBatching(this);
  1283           return DoInsertHTMLWithContext(cffragment,
  1284                                          cfcontext, cfselection, type,
  1285                                          aSourceDoc,
  1286                                          aDestinationNode, aDestOffset,
  1287                                          aDoDeleteSelection,
  1288                                          isSafe);
  1291       else if (type.EqualsLiteral(kHTMLMime)) {
  1292         nsAutoString text, contextString, infoString;
  1293         GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
  1294         GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), aIndex, contextString);
  1295         GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), aIndex, infoString);
  1297         nsAutoEditBatch beginBatching(this);
  1298         if (type.EqualsLiteral(kHTMLMime)) {
  1299           return DoInsertHTMLWithContext(text,
  1300                                          contextString, infoString, type,
  1301                                          aSourceDoc,
  1302                                          aDestinationNode, aDestOffset,
  1303                                          aDoDeleteSelection,
  1304                                          isSafe);
  1309     if (type.EqualsLiteral(kTextMime) ||
  1310         type.EqualsLiteral(kMozTextInternal)) {
  1311       nsAutoString text;
  1312       GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
  1314       nsAutoEditBatch beginBatching(this);
  1315       return InsertTextAt(text, aDestinationNode, aDestOffset, aDoDeleteSelection);
  1319   return NS_OK;
  1322 bool nsHTMLEditor::HavePrivateHTMLFlavor(nsIClipboard *aClipboard)
  1324   // check the clipboard for our special kHTMLContext flavor.  If that is there, we know
  1325   // we have our own internal html format on clipboard.
  1327   NS_ENSURE_TRUE(aClipboard, false);
  1328   bool bHavePrivateHTMLFlavor = false;
  1330   const char* flavArray[] = { kHTMLContext };
  1332   if (NS_SUCCEEDED(aClipboard->HasDataMatchingFlavors(flavArray,
  1333     ArrayLength(flavArray), nsIClipboard::kGlobalClipboard,
  1334     &bHavePrivateHTMLFlavor)))
  1335     return bHavePrivateHTMLFlavor;
  1337   return false;
  1341 NS_IMETHODIMP nsHTMLEditor::Paste(int32_t aSelectionType)
  1343   if (!FireClipboardEvent(NS_PASTE, aSelectionType))
  1344     return NS_OK;
  1346   // Get Clipboard Service
  1347   nsresult rv;
  1348   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
  1349   NS_ENSURE_SUCCESS(rv, rv);
  1351   // find out if we have our internal html flavor on the clipboard.  We don't want to mess
  1352   // around with cfhtml if we do.
  1353   bool bHavePrivateHTMLFlavor = HavePrivateHTMLFlavor(clipboard);
  1355   // Get the nsITransferable interface for getting the data from the clipboard
  1356   nsCOMPtr<nsITransferable> trans;
  1357   rv = PrepareHTMLTransferable(getter_AddRefs(trans), bHavePrivateHTMLFlavor);
  1358   NS_ENSURE_SUCCESS(rv, rv);
  1359   NS_ENSURE_TRUE(trans, NS_ERROR_FAILURE);
  1360   // Get the Data from the clipboard
  1361   rv = clipboard->GetData(trans, aSelectionType);
  1362   NS_ENSURE_SUCCESS(rv, rv);
  1363   if (!IsModifiable()) {
  1364     return NS_OK;
  1367   // also get additional html copy hints, if present
  1368   nsAutoString contextStr, infoStr;
  1370   // also get additional html copy hints, if present
  1371   if (bHavePrivateHTMLFlavor)
  1373     nsCOMPtr<nsISupports> contextDataObj, infoDataObj;
  1374     uint32_t contextLen, infoLen;
  1375     nsCOMPtr<nsISupportsString> textDataObj;
  1377     nsCOMPtr<nsITransferable> contextTrans =
  1378                   do_CreateInstance("@mozilla.org/widget/transferable;1");
  1379     NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER);
  1380     contextTrans->Init(nullptr);
  1381     contextTrans->AddDataFlavor(kHTMLContext);
  1382     clipboard->GetData(contextTrans, aSelectionType);
  1383     contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen);
  1385     nsCOMPtr<nsITransferable> infoTrans =
  1386                   do_CreateInstance("@mozilla.org/widget/transferable;1");
  1387     NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER);
  1388     infoTrans->Init(nullptr);
  1389     infoTrans->AddDataFlavor(kHTMLInfo);
  1390     clipboard->GetData(infoTrans, aSelectionType);
  1391     infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen);
  1393     if (contextDataObj)
  1395       nsAutoString text;
  1396       textDataObj = do_QueryInterface(contextDataObj);
  1397       textDataObj->GetData(text);
  1398       NS_ASSERTION(text.Length() <= (contextLen/2), "Invalid length!");
  1399       contextStr.Assign(text.get(), contextLen / 2);
  1402     if (infoDataObj)
  1404       nsAutoString text;
  1405       textDataObj = do_QueryInterface(infoDataObj);
  1406       textDataObj->GetData(text);
  1407       NS_ASSERTION(text.Length() <= (infoLen/2), "Invalid length!");
  1408       infoStr.Assign(text.get(), infoLen / 2);
  1412   // handle transferable hooks
  1413   nsCOMPtr<nsIDOMDocument> domdoc;
  1414   GetDocument(getter_AddRefs(domdoc));
  1415   if (!nsEditorHookUtils::DoInsertionHook(domdoc, nullptr, trans))
  1416     return NS_OK;
  1418   return InsertFromTransferable(trans, nullptr, contextStr, infoStr,
  1419                                 nullptr, 0, true);
  1422 NS_IMETHODIMP nsHTMLEditor::PasteTransferable(nsITransferable *aTransferable)
  1424   // Use an invalid value for the clipboard type as data comes from aTransferable
  1425   // and we don't currently implement a way to put that in the data transfer yet.
  1426   if (!FireClipboardEvent(NS_PASTE, nsIClipboard::kGlobalClipboard))
  1427     return NS_OK;
  1429   // handle transferable hooks
  1430   nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
  1431   if (!nsEditorHookUtils::DoInsertionHook(domdoc, nullptr, aTransferable))
  1432     return NS_OK;
  1434   nsAutoString contextStr, infoStr;
  1435   return InsertFromTransferable(aTransferable, nullptr, contextStr, infoStr,
  1436                                 nullptr, 0, true);
  1439 // 
  1440 // HTML PasteNoFormatting. Ignore any HTML styles and formating in paste source
  1441 //
  1442 NS_IMETHODIMP nsHTMLEditor::PasteNoFormatting(int32_t aSelectionType)
  1444   if (!FireClipboardEvent(NS_PASTE, aSelectionType))
  1445     return NS_OK;
  1447   ForceCompositionEnd();
  1449   // Get Clipboard Service
  1450   nsresult rv;
  1451   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
  1452   NS_ENSURE_SUCCESS(rv, rv);
  1454   // Get the nsITransferable interface for getting the data from the clipboard.
  1455   // use nsPlaintextEditor::PrepareTransferable() to force unicode plaintext data.
  1456   nsCOMPtr<nsITransferable> trans;
  1457   rv = nsPlaintextEditor::PrepareTransferable(getter_AddRefs(trans));
  1458   if (NS_SUCCEEDED(rv) && trans)
  1460     // Get the Data from the clipboard  
  1461     if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable())
  1463       const nsAFlatString& empty = EmptyString();
  1464       rv = InsertFromTransferable(trans, nullptr, empty, empty, nullptr, 0,
  1465                                   true);
  1469   return rv;
  1473 // The following arrays contain the MIME types that we can paste. The arrays
  1474 // are used by CanPaste() and CanPasteTransferable() below.
  1476 static const char* textEditorFlavors[] = { kUnicodeMime };
  1477 static const char* textHtmlEditorFlavors[] = { kUnicodeMime, kHTMLMime,
  1478                                                kJPEGImageMime, kJPGImageMime,
  1479                                                kPNGImageMime, kGIFImageMime };
  1481 NS_IMETHODIMP nsHTMLEditor::CanPaste(int32_t aSelectionType, bool *aCanPaste)
  1483   NS_ENSURE_ARG_POINTER(aCanPaste);
  1484   *aCanPaste = false;
  1486   // can't paste if readonly
  1487   if (!IsModifiable()) {
  1488     return NS_OK;
  1491   nsresult rv;
  1492   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
  1493   NS_ENSURE_SUCCESS(rv, rv);
  1495   bool haveFlavors;
  1497   // Use the flavors depending on the current editor mask
  1498   if (IsPlaintextEditor())
  1499     rv = clipboard->HasDataMatchingFlavors(textEditorFlavors,
  1500                                            ArrayLength(textEditorFlavors),
  1501                                            aSelectionType, &haveFlavors);
  1502   else
  1503     rv = clipboard->HasDataMatchingFlavors(textHtmlEditorFlavors,
  1504                                            ArrayLength(textHtmlEditorFlavors),
  1505                                            aSelectionType, &haveFlavors);
  1507   NS_ENSURE_SUCCESS(rv, rv);
  1509   *aCanPaste = haveFlavors;
  1510   return NS_OK;
  1513 NS_IMETHODIMP nsHTMLEditor::CanPasteTransferable(nsITransferable *aTransferable, bool *aCanPaste)
  1515   NS_ENSURE_ARG_POINTER(aCanPaste);
  1517   // can't paste if readonly
  1518   if (!IsModifiable()) {
  1519     *aCanPaste = false;
  1520     return NS_OK;
  1523   // If |aTransferable| is null, assume that a paste will succeed.
  1524   if (!aTransferable) {
  1525     *aCanPaste = true;
  1526     return NS_OK;
  1529   // Peek in |aTransferable| to see if it contains a supported MIME type.
  1531   // Use the flavors depending on the current editor mask
  1532   const char ** flavors;
  1533   unsigned length;
  1534   if (IsPlaintextEditor()) {
  1535     flavors = textEditorFlavors;
  1536     length = ArrayLength(textEditorFlavors);
  1537   } else {
  1538     flavors = textHtmlEditorFlavors;
  1539     length = ArrayLength(textHtmlEditorFlavors);
  1542   for (unsigned int i = 0; i < length; i++, flavors++) {
  1543     nsCOMPtr<nsISupports> data;
  1544     uint32_t dataLen;
  1545     nsresult rv = aTransferable->GetTransferData(*flavors,
  1546                                                  getter_AddRefs(data),
  1547                                                  &dataLen);
  1548     if (NS_SUCCEEDED(rv) && data) {
  1549       *aCanPaste = true;
  1550       return NS_OK;
  1554   *aCanPaste = false;
  1555   return NS_OK;
  1559 //
  1560 // HTML PasteAsQuotation: Paste in a blockquote type=cite
  1561 //
  1562 NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation(int32_t aSelectionType)
  1564   if (IsPlaintextEditor())
  1565     return PasteAsPlaintextQuotation(aSelectionType);
  1567   nsAutoString citation;
  1568   return PasteAsCitedQuotation(citation, aSelectionType);
  1571 NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsAString & aCitation,
  1572                                                   int32_t aSelectionType)
  1574   nsAutoEditBatch beginBatching(this);
  1575   nsAutoRules beginRulesSniffing(this, EditAction::insertQuotation, nsIEditor::eNext);
  1577   // get selection
  1578   nsRefPtr<Selection> selection = GetSelection();
  1579   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1581   // give rules a chance to handle or cancel
  1582   nsTextRulesInfo ruleInfo(EditAction::insertElement);
  1583   bool cancel, handled;
  1584   // Protect the edit rules object from dying
  1585   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1586   nsresult rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
  1587   NS_ENSURE_SUCCESS(rv, rv);
  1588   if (cancel || handled) {
  1589     return NS_OK; // rules canceled the operation
  1592   nsCOMPtr<nsIDOMNode> newNode;
  1593   rv = DeleteSelectionAndCreateNode(NS_LITERAL_STRING("blockquote"), getter_AddRefs(newNode));
  1594   NS_ENSURE_SUCCESS(rv, rv);
  1595   NS_ENSURE_TRUE(newNode, NS_ERROR_NULL_POINTER);
  1597   // Try to set type=cite.  Ignore it if this fails.
  1598   nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(newNode);
  1599   if (newElement) {
  1600     newElement->SetAttribute(NS_LITERAL_STRING("type"), NS_LITERAL_STRING("cite"));
  1603   // Set the selection to the underneath the node we just inserted:
  1604   rv = selection->Collapse(newNode, 0);
  1605   NS_ENSURE_SUCCESS(rv, rv);
  1607   return Paste(aSelectionType);
  1610 //
  1611 // Paste a plaintext quotation
  1612 //
  1613 NS_IMETHODIMP nsHTMLEditor::PasteAsPlaintextQuotation(int32_t aSelectionType)
  1615   // Get Clipboard Service
  1616   nsresult rv;
  1617   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
  1618   NS_ENSURE_SUCCESS(rv, rv);
  1620   // Create generic Transferable for getting the data
  1621   nsCOMPtr<nsITransferable> trans =
  1622                  do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
  1623   NS_ENSURE_SUCCESS(rv, rv);
  1624   NS_ENSURE_TRUE(trans, NS_ERROR_FAILURE);
  1626   nsCOMPtr<nsIDocument> destdoc = GetDocument();
  1627   nsILoadContext* loadContext = destdoc ? destdoc->GetLoadContext() : nullptr;
  1628   trans->Init(loadContext);
  1630   // We only handle plaintext pastes here
  1631   trans->AddDataFlavor(kUnicodeMime);
  1633   // Get the Data from the clipboard
  1634   clipboard->GetData(trans, aSelectionType);
  1636   // Now we ask the transferable for the data
  1637   // it still owns the data, we just have a pointer to it.
  1638   // If it can't support a "text" output of the data the call will fail
  1639   nsCOMPtr<nsISupports> genericDataObj;
  1640   uint32_t len = 0;
  1641   char* flav = 0;
  1642   rv = trans->GetAnyTransferData(&flav, getter_AddRefs(genericDataObj), &len);
  1643   NS_ENSURE_SUCCESS(rv, rv);
  1645   if (flav && 0 == nsCRT::strcmp(flav, kUnicodeMime))
  1647 #ifdef DEBUG_clipboard
  1648     printf("Got flavor [%s]\n", flav);
  1649 #endif
  1650     nsCOMPtr<nsISupportsString> textDataObj = do_QueryInterface(genericDataObj);
  1651     if (textDataObj && len > 0)
  1653       nsAutoString stuffToPaste;
  1654       textDataObj->GetData(stuffToPaste);
  1655       NS_ASSERTION(stuffToPaste.Length() <= (len/2), "Invalid length!");
  1656       nsAutoEditBatch beginBatching(this);
  1657       rv = InsertAsPlaintextQuotation(stuffToPaste, true, 0);
  1660   NS_Free(flav);
  1662   return rv;
  1665 NS_IMETHODIMP
  1666 nsHTMLEditor::InsertTextWithQuotations(const nsAString &aStringToInsert)
  1668   if (mWrapToWindow)
  1669     return InsertText(aStringToInsert);
  1671   // The whole operation should be undoable in one transaction:
  1672   BeginTransaction();
  1674   // We're going to loop over the string, collecting up a "hunk"
  1675   // that's all the same type (quoted or not),
  1676   // Whenever the quotedness changes (or we reach the string's end)
  1677   // we will insert the hunk all at once, quoted or non.
  1679   static const char16_t cite('>');
  1680   bool curHunkIsQuoted = (aStringToInsert.First() == cite);
  1682   nsAString::const_iterator hunkStart, strEnd;
  1683   aStringToInsert.BeginReading(hunkStart);
  1684   aStringToInsert.EndReading(strEnd);
  1686   // In the loop below, we only look for DOM newlines (\n),
  1687   // because we don't have a FindChars method that can look
  1688   // for both \r and \n.  \r is illegal in the dom anyway,
  1689   // but in debug builds, let's take the time to verify that
  1690   // there aren't any there:
  1691 #ifdef DEBUG
  1692   nsAString::const_iterator dbgStart (hunkStart);
  1693   if (FindCharInReadable('\r', dbgStart, strEnd))
  1694     NS_ASSERTION(false,
  1695             "Return characters in DOM! InsertTextWithQuotations may be wrong");
  1696 #endif /* DEBUG */
  1698   // Loop over lines:
  1699   nsresult rv = NS_OK;
  1700   nsAString::const_iterator lineStart (hunkStart);
  1701   while (1)   // we will break from inside when we run out of newlines
  1703     // Search for the end of this line (dom newlines, see above):
  1704     bool found = FindCharInReadable('\n', lineStart, strEnd);
  1705     bool quoted = false;
  1706     if (found)
  1708       // if there's another newline, lineStart now points there.
  1709       // Loop over any consecutive newline chars:
  1710       nsAString::const_iterator firstNewline (lineStart);
  1711       while (*lineStart == '\n')
  1712         ++lineStart;
  1713       quoted = (*lineStart == cite);
  1714       if (quoted == curHunkIsQuoted)
  1715         continue;
  1716       // else we're changing state, so we need to insert
  1717       // from curHunk to lineStart then loop around.
  1719       // But if the current hunk is quoted, then we want to make sure
  1720       // that any extra newlines on the end do not get included in
  1721       // the quoted section: blank lines flaking a quoted section
  1722       // should be considered unquoted, so that if the user clicks
  1723       // there and starts typing, the new text will be outside of
  1724       // the quoted block.
  1725       if (curHunkIsQuoted)
  1726         lineStart = firstNewline;
  1729     // If no newline found, lineStart is now strEnd and we can finish up,
  1730     // inserting from curHunk to lineStart then returning.
  1731     const nsAString &curHunk = Substring(hunkStart, lineStart);
  1732     nsCOMPtr<nsIDOMNode> dummyNode;
  1733 #ifdef DEBUG_akkana_verbose
  1734     printf("==== Inserting text as %squoted: ---\n%s---\n",
  1735            curHunkIsQuoted ? "" : "non-",
  1736            NS_LossyConvertUTF16toASCII(curHunk).get());
  1737 #endif
  1738     if (curHunkIsQuoted)
  1739       rv = InsertAsPlaintextQuotation(curHunk, false,
  1740                                       getter_AddRefs(dummyNode));
  1741     else
  1742       rv = InsertText(curHunk);
  1744     if (!found)
  1745       break;
  1747     curHunkIsQuoted = quoted;
  1748     hunkStart = lineStart;
  1751   EndTransaction();
  1753   return rv;
  1756 NS_IMETHODIMP nsHTMLEditor::InsertAsQuotation(const nsAString & aQuotedText,
  1757                                               nsIDOMNode **aNodeInserted)
  1759   if (IsPlaintextEditor())
  1760     return InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted);
  1762   nsAutoString citation;
  1763   return InsertAsCitedQuotation(aQuotedText, citation, false,
  1764                                 aNodeInserted);
  1767 // Insert plaintext as a quotation, with cite marks (e.g. "> ").
  1768 // This differs from its corresponding method in nsPlaintextEditor
  1769 // in that here, quoted material is enclosed in a <pre> tag
  1770 // in order to preserve the original line wrapping.
  1771 NS_IMETHODIMP
  1772 nsHTMLEditor::InsertAsPlaintextQuotation(const nsAString & aQuotedText,
  1773                                          bool aAddCites,
  1774                                          nsIDOMNode **aNodeInserted)
  1776   if (mWrapToWindow)
  1777     return nsPlaintextEditor::InsertAsQuotation(aQuotedText, aNodeInserted);
  1779   nsCOMPtr<nsIDOMNode> newNode;
  1780   // get selection
  1781   nsRefPtr<Selection> selection = GetSelection();
  1782   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1784   nsAutoEditBatch beginBatching(this);
  1785   nsAutoRules beginRulesSniffing(this, EditAction::insertQuotation, nsIEditor::eNext);
  1787   // give rules a chance to handle or cancel
  1788   nsTextRulesInfo ruleInfo(EditAction::insertElement);
  1789   bool cancel, handled;
  1790   // Protect the edit rules object from dying
  1791   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1792   nsresult rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
  1793   NS_ENSURE_SUCCESS(rv, rv);
  1794   if (cancel || handled) {
  1795     return NS_OK; // rules canceled the operation
  1798   // Wrap the inserted quote in a <span> so it won't be wrapped:
  1799   rv = DeleteSelectionAndCreateNode(NS_LITERAL_STRING("span"), getter_AddRefs(newNode));
  1801   // If this succeeded, then set selection inside the pre
  1802   // so the inserted text will end up there.
  1803   // If it failed, we don't care what the return value was,
  1804   // but we'll fall through and try to insert the text anyway.
  1805   if (NS_SUCCEEDED(rv) && newNode)
  1807     // Add an attribute on the pre node so we'll know it's a quotation.
  1808     // Do this after the insertion, so that
  1809     nsCOMPtr<nsIDOMElement> preElement = do_QueryInterface(newNode);
  1810     if (preElement)
  1812       preElement->SetAttribute(NS_LITERAL_STRING("_moz_quote"),
  1813                                NS_LITERAL_STRING("true"));
  1814       // turn off wrapping on spans
  1815       preElement->SetAttribute(NS_LITERAL_STRING("style"),
  1816                                NS_LITERAL_STRING("white-space: pre;"));
  1818     // and set the selection inside it:
  1819     selection->Collapse(newNode, 0);
  1822   if (aAddCites)
  1823     rv = nsPlaintextEditor::InsertAsQuotation(aQuotedText, aNodeInserted);
  1824   else
  1825     rv = nsPlaintextEditor::InsertText(aQuotedText);
  1826   // Note that if !aAddCites, aNodeInserted isn't set.
  1827   // That's okay because the routines that use aAddCites
  1828   // don't need to know the inserted node.
  1830   if (aNodeInserted && NS_SUCCEEDED(rv))
  1832     *aNodeInserted = newNode;
  1833     NS_IF_ADDREF(*aNodeInserted);
  1836   // Set the selection to just after the inserted node:
  1837   if (NS_SUCCEEDED(rv) && newNode)
  1839     int32_t offset;
  1840     nsCOMPtr<nsIDOMNode> parent = GetNodeLocation(newNode, &offset);
  1841     if (parent) {
  1842       selection->Collapse(parent, offset + 1);
  1845   return rv;
  1848 NS_IMETHODIMP
  1849 nsHTMLEditor::StripCites()
  1851   return nsPlaintextEditor::StripCites();
  1854 NS_IMETHODIMP
  1855 nsHTMLEditor::Rewrap(bool aRespectNewlines)
  1857   return nsPlaintextEditor::Rewrap(aRespectNewlines);
  1860 NS_IMETHODIMP
  1861 nsHTMLEditor::InsertAsCitedQuotation(const nsAString & aQuotedText,
  1862                                      const nsAString & aCitation,
  1863                                      bool aInsertHTML,
  1864                                      nsIDOMNode **aNodeInserted)
  1866   // Don't let anyone insert html into a "plaintext" editor:
  1867   if (IsPlaintextEditor())
  1869     NS_ASSERTION(!aInsertHTML, "InsertAsCitedQuotation: trying to insert html into plaintext editor");
  1870     return InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted);
  1873   nsCOMPtr<nsIDOMNode> newNode;
  1875   // get selection
  1876   nsRefPtr<Selection> selection = GetSelection();
  1877   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1879   nsAutoEditBatch beginBatching(this);
  1880   nsAutoRules beginRulesSniffing(this, EditAction::insertQuotation, nsIEditor::eNext);
  1882   // give rules a chance to handle or cancel
  1883   nsTextRulesInfo ruleInfo(EditAction::insertElement);
  1884   bool cancel, handled;
  1885   // Protect the edit rules object from dying
  1886   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1887   nsresult rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
  1888   NS_ENSURE_SUCCESS(rv, rv);
  1889   if (cancel || handled) {
  1890     return NS_OK; // rules canceled the operation
  1893   rv = DeleteSelectionAndCreateNode(NS_LITERAL_STRING("blockquote"), getter_AddRefs(newNode));
  1894   NS_ENSURE_SUCCESS(rv, rv);
  1895   NS_ENSURE_TRUE(newNode, NS_ERROR_NULL_POINTER);
  1897   // Try to set type=cite.  Ignore it if this fails.
  1898   nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(newNode);
  1899   if (newElement)
  1901     NS_NAMED_LITERAL_STRING(citeStr, "cite");
  1902     newElement->SetAttribute(NS_LITERAL_STRING("type"), citeStr);
  1904     if (!aCitation.IsEmpty())
  1905       newElement->SetAttribute(citeStr, aCitation);
  1907     // Set the selection inside the blockquote so aQuotedText will go there:
  1908     selection->Collapse(newNode, 0);
  1911   if (aInsertHTML)
  1912     rv = LoadHTML(aQuotedText);
  1913   else
  1914     rv = InsertText(aQuotedText);  // XXX ignore charset
  1916   if (aNodeInserted && NS_SUCCEEDED(rv))
  1918     *aNodeInserted = newNode;
  1919     NS_IF_ADDREF(*aNodeInserted);
  1922   // Set the selection to just after the inserted node:
  1923   if (NS_SUCCEEDED(rv) && newNode)
  1925     int32_t offset;
  1926     nsCOMPtr<nsIDOMNode> parent = GetNodeLocation(newNode, &offset);
  1927     if (parent) {
  1928       selection->Collapse(parent, offset + 1);
  1931   return rv;
  1935 void RemoveBodyAndHead(nsIDOMNode *aNode)
  1937   if (!aNode)
  1938     return;
  1940   nsCOMPtr<nsIDOMNode> tmp, child, body, head;
  1941   // find the body and head nodes if any.
  1942   // look only at immediate children of aNode.
  1943   aNode->GetFirstChild(getter_AddRefs(child));
  1944   while (child)
  1946     if (nsTextEditUtils::IsBody(child))
  1948       body = child;
  1950     else if (nsEditor::NodeIsType(child, nsEditProperty::head))
  1952       head = child;
  1954     child->GetNextSibling(getter_AddRefs(tmp));
  1955     child = tmp;
  1957   if (head)
  1959     aNode->RemoveChild(head, getter_AddRefs(tmp));
  1961   if (body)
  1963     body->GetFirstChild(getter_AddRefs(child));
  1964     while (child)
  1966       aNode->InsertBefore(child, body, getter_AddRefs(tmp));
  1967       body->GetFirstChild(getter_AddRefs(child));
  1969     aNode->RemoveChild(body, getter_AddRefs(tmp));
  1973 /**
  1974  * This function finds the target node that we will be pasting into. aStart is
  1975  * the context that we're given and aResult will be the target. Initially,
  1976  * *aResult must be nullptr.
  1978  * The target for a paste is found by either finding the node that contains
  1979  * the magical comment node containing kInsertCookie or, failing that, the
  1980  * firstChild of the firstChild (until we reach a leaf).
  1981  */
  1982 nsresult FindTargetNode(nsIDOMNode *aStart, nsCOMPtr<nsIDOMNode> &aResult)
  1984   NS_ENSURE_TRUE(aStart, NS_OK);
  1986   nsCOMPtr<nsIDOMNode> child, tmp;
  1988   nsresult rv = aStart->GetFirstChild(getter_AddRefs(child));
  1989   NS_ENSURE_SUCCESS(rv, rv);
  1991   if (!child)
  1993     // If the current result is nullptr, then aStart is a leaf, and is the
  1994     // fallback result.
  1995     if (!aResult)
  1996       aResult = aStart;
  1998     return NS_OK;
  2001   do
  2003     // Is this child the magical cookie?
  2004     nsCOMPtr<nsIDOMComment> comment = do_QueryInterface(child);
  2005     if (comment)
  2007       nsAutoString data;
  2008       rv = comment->GetData(data);
  2009       NS_ENSURE_SUCCESS(rv, rv);
  2011       if (data.EqualsLiteral(kInsertCookie))
  2013         // Yes it is! Return an error so we bubble out and short-circuit the
  2014         // search.
  2015         aResult = aStart;
  2017         // Note: it doesn't matter if this fails.
  2018         aStart->RemoveChild(child, getter_AddRefs(tmp));
  2020         return NS_FOUND_TARGET;
  2024     // Note: Don't use NS_ENSURE_* here since we return a failure result to
  2025     // inicate that we found the magical cookie and we don't want to spam the
  2026     // console.
  2027     rv = FindTargetNode(child, aResult);
  2028     NS_ENSURE_SUCCESS(rv, rv);
  2030     rv = child->GetNextSibling(getter_AddRefs(tmp));
  2031     NS_ENSURE_SUCCESS(rv, rv);
  2033     child = tmp;
  2034   } while (child);
  2036   return NS_OK;
  2039 nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(const nsAString &aInputString,
  2040                                                   const nsAString & aContextStr,
  2041                                                   const nsAString & aInfoStr,
  2042                                                   nsCOMPtr<nsIDOMNode> *outFragNode,
  2043                                                   nsCOMPtr<nsIDOMNode> *outStartNode,
  2044                                                   nsCOMPtr<nsIDOMNode> *outEndNode,
  2045                                                   int32_t *outStartOffset,
  2046                                                   int32_t *outEndOffset,
  2047                                                   bool aTrustedInput)
  2049   NS_ENSURE_TRUE(outFragNode && outStartNode && outEndNode, NS_ERROR_NULL_POINTER);
  2050   nsCOMPtr<nsIDOMDocumentFragment> docfrag;
  2051   nsCOMPtr<nsIDOMNode> contextAsNode, tmp;
  2052   nsresult rv = NS_OK;
  2054   nsCOMPtr<nsIDocument> doc = GetDocument();
  2055   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
  2057   // if we have context info, create a fragment for that
  2058   nsCOMPtr<nsIDOMDocumentFragment> contextfrag;
  2059   nsCOMPtr<nsIDOMNode> contextLeaf, junk;
  2060   if (!aContextStr.IsEmpty())
  2062     rv = ParseFragment(aContextStr, nullptr, doc, address_of(contextAsNode),
  2063                        aTrustedInput);
  2064     NS_ENSURE_SUCCESS(rv, rv);
  2065     NS_ENSURE_TRUE(contextAsNode, NS_ERROR_FAILURE);
  2067     rv = StripFormattingNodes(contextAsNode);
  2068     NS_ENSURE_SUCCESS(rv, rv);
  2070     RemoveBodyAndHead(contextAsNode);
  2072     rv = FindTargetNode(contextAsNode, contextLeaf);
  2073     if (rv == NS_FOUND_TARGET) {
  2074       rv = NS_OK;
  2076     NS_ENSURE_SUCCESS(rv, rv);
  2079   nsCOMPtr<nsIContent> contextLeafAsContent = do_QueryInterface(contextLeaf);
  2081   // create fragment for pasted html
  2082   nsIAtom* contextAtom;
  2083   if (contextLeafAsContent) {
  2084     contextAtom = contextLeafAsContent->Tag();
  2085     if (contextAtom == nsGkAtoms::html) {
  2086       contextAtom = nsGkAtoms::body;
  2088   } else {
  2089     contextAtom = nsGkAtoms::body;
  2091   rv = ParseFragment(aInputString,
  2092                      contextAtom,
  2093                      doc,
  2094                      outFragNode,
  2095                      aTrustedInput);
  2096   NS_ENSURE_SUCCESS(rv, rv);
  2097   NS_ENSURE_TRUE(*outFragNode, NS_ERROR_FAILURE);
  2099   RemoveBodyAndHead(*outFragNode);
  2101   if (contextAsNode)
  2103     // unite the two trees
  2104     contextLeaf->AppendChild(*outFragNode, getter_AddRefs(junk));
  2105     *outFragNode = contextAsNode;
  2108   rv = StripFormattingNodes(*outFragNode, true);
  2109   NS_ENSURE_SUCCESS(rv, rv);
  2111   // If there was no context, then treat all of the data we did get as the
  2112   // pasted data.
  2113   if (contextLeaf)
  2114     *outEndNode = *outStartNode = contextLeaf;
  2115   else
  2116     *outEndNode = *outStartNode = *outFragNode;
  2118   *outStartOffset = 0;
  2120   // get the infoString contents
  2121   nsAutoString numstr1, numstr2;
  2122   if (!aInfoStr.IsEmpty())
  2124     int32_t sep, num;
  2125     sep = aInfoStr.FindChar((char16_t)',');
  2126     numstr1 = Substring(aInfoStr, 0, sep);
  2127     numstr2 = Substring(aInfoStr, sep+1, aInfoStr.Length() - (sep+1));
  2129     // Move the start and end children.
  2130     nsresult err;
  2131     num = numstr1.ToInteger(&err);
  2132     while (num--)
  2134       (*outStartNode)->GetFirstChild(getter_AddRefs(tmp));
  2135       NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
  2136       tmp.swap(*outStartNode);
  2139     num = numstr2.ToInteger(&err);
  2140     while (num--)
  2142       (*outEndNode)->GetLastChild(getter_AddRefs(tmp));
  2143       NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
  2144       tmp.swap(*outEndNode);
  2148   GetLengthOfDOMNode(*outEndNode, (uint32_t&)*outEndOffset);
  2149   return NS_OK;
  2153 nsresult nsHTMLEditor::ParseFragment(const nsAString & aFragStr,
  2154                                      nsIAtom* aContextLocalName,
  2155                                      nsIDocument* aTargetDocument,
  2156                                      nsCOMPtr<nsIDOMNode> *outNode,
  2157                                      bool aTrustedInput)
  2159   nsAutoScriptBlockerSuppressNodeRemoved autoBlocker;
  2161   nsRefPtr<DocumentFragment> fragment =
  2162     new DocumentFragment(aTargetDocument->NodeInfoManager());
  2163   nsresult rv = nsContentUtils::ParseFragmentHTML(aFragStr,
  2164                                                   fragment,
  2165                                                   aContextLocalName ?
  2166                                                     aContextLocalName : nsGkAtoms::body,
  2167                                                     kNameSpaceID_XHTML,
  2168                                                   false,
  2169                                                   true);
  2170   if (!aTrustedInput) {
  2171     nsTreeSanitizer sanitizer(aContextLocalName ?
  2172                               nsIParserUtils::SanitizerAllowStyle :
  2173                               nsIParserUtils::SanitizerAllowComments);
  2174     sanitizer.Sanitize(fragment);
  2176   *outNode = fragment.forget();
  2177   return rv;
  2180 nsresult nsHTMLEditor::CreateListOfNodesToPaste(nsIDOMNode  *aFragmentAsNode,
  2181                                                 nsCOMArray<nsIDOMNode>& outNodeList,
  2182                                                 nsIDOMNode *aStartNode,
  2183                                                 int32_t aStartOffset,
  2184                                                 nsIDOMNode *aEndNode,
  2185                                                 int32_t aEndOffset)
  2187   NS_ENSURE_TRUE(aFragmentAsNode, NS_ERROR_NULL_POINTER);
  2189   nsresult rv;
  2191   // if no info was provided about the boundary between context and stream,
  2192   // then assume all is stream.
  2193   if (!aStartNode)
  2195     int32_t fragLen;
  2196     rv = GetLengthOfDOMNode(aFragmentAsNode, (uint32_t&)fragLen);
  2197     NS_ENSURE_SUCCESS(rv, rv);
  2199     aStartNode = aFragmentAsNode;
  2200     aStartOffset = 0;
  2201     aEndNode = aFragmentAsNode;
  2202     aEndOffset = fragLen;
  2205   nsRefPtr<nsRange> docFragRange;
  2206   rv = nsRange::CreateRange(aStartNode, aStartOffset, aEndNode, aEndOffset, getter_AddRefs(docFragRange));
  2207   NS_ENSURE_SUCCESS(rv, rv);
  2209   // now use a subtree iterator over the range to create a list of nodes
  2210   nsTrivialFunctor functor;
  2211   nsDOMSubtreeIterator iter;
  2212   rv = iter.Init(docFragRange);
  2213   NS_ENSURE_SUCCESS(rv, rv);
  2215   return iter.AppendList(functor, outNodeList);
  2218 nsresult
  2219 nsHTMLEditor::GetListAndTableParents(bool aEnd,
  2220                                      nsCOMArray<nsIDOMNode>& aListOfNodes,
  2221                                      nsCOMArray<nsIDOMNode>& outArray)
  2223   int32_t listCount = aListOfNodes.Count();
  2224   NS_ENSURE_TRUE(listCount > 0, NS_ERROR_FAILURE);  // no empty lists, please
  2226   // build up list of parents of first (or last) node in list
  2227   // that are either lists, or tables.
  2228   int32_t idx = 0;
  2229   if (aEnd) idx = listCount-1;
  2231   nsCOMPtr<nsIDOMNode> pNode = aListOfNodes[idx];
  2232   while (pNode)
  2234     if (nsHTMLEditUtils::IsList(pNode) || nsHTMLEditUtils::IsTable(pNode))
  2236       NS_ENSURE_TRUE(outArray.AppendObject(pNode), NS_ERROR_FAILURE);
  2238     nsCOMPtr<nsIDOMNode> parent;
  2239     pNode->GetParentNode(getter_AddRefs(parent));
  2240     pNode = parent;
  2242   return NS_OK;
  2245 nsresult
  2246 nsHTMLEditor::DiscoverPartialListsAndTables(nsCOMArray<nsIDOMNode>& aPasteNodes,
  2247                                             nsCOMArray<nsIDOMNode>& aListsAndTables,
  2248                                             int32_t *outHighWaterMark)
  2250   NS_ENSURE_TRUE(outHighWaterMark, NS_ERROR_NULL_POINTER);
  2252   *outHighWaterMark = -1;
  2253   int32_t listAndTableParents = aListsAndTables.Count();
  2255   // scan insertion list for table elements (other than table).
  2256   int32_t listCount = aPasteNodes.Count();
  2257   int32_t j;  
  2258   for (j=0; j<listCount; j++)
  2260     nsCOMPtr<nsIDOMNode> curNode = aPasteNodes[j];
  2262     NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE);
  2263     if (nsHTMLEditUtils::IsTableElement(curNode) && !nsHTMLEditUtils::IsTable(curNode))
  2265       nsCOMPtr<nsIDOMNode> theTable = GetTableParent(curNode);
  2266       if (theTable)
  2268         int32_t indexT = aListsAndTables.IndexOf(theTable);
  2269         if (indexT >= 0)
  2271           *outHighWaterMark = indexT;
  2272           if (*outHighWaterMark == listAndTableParents-1) break;
  2274         else
  2276           break;
  2280     if (nsHTMLEditUtils::IsListItem(curNode))
  2282       nsCOMPtr<nsIDOMNode> theList = GetListParent(curNode);
  2283       if (theList)
  2285         int32_t indexL = aListsAndTables.IndexOf(theList);
  2286         if (indexL >= 0)
  2288           *outHighWaterMark = indexL;
  2289           if (*outHighWaterMark == listAndTableParents-1) break;
  2291         else
  2293           break;
  2298   return NS_OK;
  2301 nsresult
  2302 nsHTMLEditor::ScanForListAndTableStructure( bool aEnd,
  2303                                             nsCOMArray<nsIDOMNode>& aNodes,
  2304                                             nsIDOMNode *aListOrTable,
  2305                                             nsCOMPtr<nsIDOMNode> *outReplaceNode)
  2307   NS_ENSURE_TRUE(aListOrTable, NS_ERROR_NULL_POINTER);
  2308   NS_ENSURE_TRUE(outReplaceNode, NS_ERROR_NULL_POINTER);
  2310   *outReplaceNode = 0;
  2312   // look upward from first/last paste node for a piece of this list/table
  2313   int32_t listCount = aNodes.Count(), idx = 0;
  2314   if (aEnd) idx = listCount-1;
  2315   bool bList = nsHTMLEditUtils::IsList(aListOrTable);
  2317   nsCOMPtr<nsIDOMNode>  pNode = aNodes[idx];
  2318   nsCOMPtr<nsIDOMNode>  originalNode = pNode;
  2319   while (pNode)
  2321     if ((bList && nsHTMLEditUtils::IsListItem(pNode)) ||
  2322         (!bList && (nsHTMLEditUtils::IsTableElement(pNode) && !nsHTMLEditUtils::IsTable(pNode))))
  2324       nsCOMPtr<nsIDOMNode> structureNode;
  2325       if (bList) structureNode = GetListParent(pNode);
  2326       else structureNode = GetTableParent(pNode);
  2327       if (structureNode == aListOrTable)
  2329         if (bList)
  2330           *outReplaceNode = structureNode;
  2331         else
  2332           *outReplaceNode = pNode;
  2333         break;
  2336     nsCOMPtr<nsIDOMNode> parent;
  2337     pNode->GetParentNode(getter_AddRefs(parent));
  2338     pNode = parent;
  2340   return NS_OK;
  2343 nsresult
  2344 nsHTMLEditor::ReplaceOrphanedStructure(bool aEnd,
  2345                                        nsCOMArray<nsIDOMNode>& aNodeArray,
  2346                                        nsCOMArray<nsIDOMNode>& aListAndTableArray,
  2347                                        int32_t aHighWaterMark)
  2349   nsCOMPtr<nsIDOMNode> curNode = aListAndTableArray[aHighWaterMark];
  2350   NS_ENSURE_TRUE(curNode, NS_ERROR_NULL_POINTER);
  2352   nsCOMPtr<nsIDOMNode> replaceNode, originalNode;
  2354   // find substructure of list or table that must be included in paste.
  2355   nsresult rv = ScanForListAndTableStructure(aEnd, aNodeArray,
  2356                                  curNode, address_of(replaceNode));
  2357   NS_ENSURE_SUCCESS(rv, rv);
  2359   // if we found substructure, paste it instead of its descendants
  2360   if (replaceNode)
  2362     // postprocess list to remove any descendants of this node
  2363     // so that we don't insert them twice.
  2364     nsCOMPtr<nsIDOMNode> endpoint;
  2365     do
  2367       endpoint = GetArrayEndpoint(aEnd, aNodeArray);
  2368       if (!endpoint) break;
  2369       if (nsEditorUtils::IsDescendantOf(endpoint, replaceNode))
  2370         aNodeArray.RemoveObject(endpoint);
  2371       else
  2372         break;
  2373     } while(endpoint);
  2375     // now replace the removed nodes with the structural parent
  2376     if (aEnd) aNodeArray.AppendObject(replaceNode);
  2377     else aNodeArray.InsertObjectAt(replaceNode, 0);
  2379   return NS_OK;
  2382 nsIDOMNode* nsHTMLEditor::GetArrayEndpoint(bool aEnd,
  2383                                            nsCOMArray<nsIDOMNode>& aNodeArray)
  2385   int32_t listCount = aNodeArray.Count();
  2386   if (listCount <= 0) {
  2387     return nullptr;
  2390   if (aEnd) {
  2391     return aNodeArray[listCount-1];
  2394   return aNodeArray[0];

mercurial