editor/libeditor/html/nsHTMLDataTransfer.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2395 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 sw=2 et tw=78: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include <string.h>
    1.11 +
    1.12 +#include "mozilla/dom/DocumentFragment.h"
    1.13 +#include "mozilla/ArrayUtils.h"
    1.14 +#include "mozilla/Base64.h"
    1.15 +#include "mozilla/BasicEvents.h"
    1.16 +#include "mozilla/Preferences.h"
    1.17 +#include "mozilla/dom/Selection.h"
    1.18 +#include "nsAString.h"
    1.19 +#include "nsAutoPtr.h"
    1.20 +#include "nsCOMArray.h"
    1.21 +#include "nsCOMPtr.h"
    1.22 +#include "nsCRT.h"
    1.23 +#include "nsCRTGlue.h"
    1.24 +#include "nsComponentManagerUtils.h"
    1.25 +#include "nsContentUtils.h"
    1.26 +#include "nsDebug.h"
    1.27 +#include "nsDependentSubstring.h"
    1.28 +#include "nsEditProperty.h"
    1.29 +#include "nsEditRules.h"
    1.30 +#include "nsEditor.h"
    1.31 +#include "nsEditorUtils.h"
    1.32 +#include "nsError.h"
    1.33 +#include "nsGkAtoms.h"
    1.34 +#include "nsHTMLEditUtils.h"
    1.35 +#include "nsHTMLEditor.h"
    1.36 +#include "nsIClipboard.h"
    1.37 +#include "nsIContent.h"
    1.38 +#include "nsIContentFilter.h"
    1.39 +#include "nsIDOMComment.h"
    1.40 +#include "mozilla/dom/DOMStringList.h"
    1.41 +#include "mozilla/dom/DataTransfer.h"
    1.42 +#include "nsIDOMDocument.h"
    1.43 +#include "nsIDOMDocumentFragment.h"
    1.44 +#include "nsIDOMElement.h"
    1.45 +#include "nsIDOMHTMLAnchorElement.h"
    1.46 +#include "nsIDOMHTMLEmbedElement.h"
    1.47 +#include "nsIDOMHTMLFrameElement.h"
    1.48 +#include "nsIDOMHTMLIFrameElement.h"
    1.49 +#include "nsIDOMHTMLImageElement.h"
    1.50 +#include "nsIDOMHTMLInputElement.h"
    1.51 +#include "nsIDOMHTMLLinkElement.h"
    1.52 +#include "nsIDOMHTMLObjectElement.h"
    1.53 +#include "nsIDOMHTMLScriptElement.h"
    1.54 +#include "nsIDOMNode.h"
    1.55 +#include "nsIDOMRange.h"
    1.56 +#include "nsIDocument.h"
    1.57 +#include "nsIEditor.h"
    1.58 +#include "nsIEditorIMESupport.h"
    1.59 +#include "nsIEditorMailSupport.h"
    1.60 +#include "nsIFile.h"
    1.61 +#include "nsIInputStream.h"
    1.62 +#include "nsIMIMEService.h"
    1.63 +#include "nsNameSpaceManager.h"
    1.64 +#include "nsINode.h"
    1.65 +#include "nsIParserUtils.h"
    1.66 +#include "nsIPlaintextEditor.h"
    1.67 +#include "nsISelection.h"
    1.68 +#include "nsISupportsImpl.h"
    1.69 +#include "nsISupportsPrimitives.h"
    1.70 +#include "nsISupportsUtils.h"
    1.71 +#include "nsITransferable.h"
    1.72 +#include "nsIURI.h"
    1.73 +#include "nsIVariant.h"
    1.74 +#include "nsLinebreakConverter.h"
    1.75 +#include "nsLiteralString.h"
    1.76 +#include "nsNetUtil.h"
    1.77 +#include "nsPlaintextEditor.h"
    1.78 +#include "nsRange.h"
    1.79 +#include "nsReadableUtils.h"
    1.80 +#include "nsSelectionState.h"
    1.81 +#include "nsServiceManagerUtils.h"
    1.82 +#include "nsStreamUtils.h"
    1.83 +#include "nsString.h"
    1.84 +#include "nsStringFwd.h"
    1.85 +#include "nsStringIterator.h"
    1.86 +#include "nsSubstringTuple.h"
    1.87 +#include "nsTextEditRules.h"
    1.88 +#include "nsTextEditUtils.h"
    1.89 +#include "nsTreeSanitizer.h"
    1.90 +#include "nsWSRunObject.h"
    1.91 +#include "nsXPCOM.h"
    1.92 +#include "nscore.h"
    1.93 +
    1.94 +class nsIAtom;
    1.95 +class nsILoadContext;
    1.96 +class nsISupports;
    1.97 +
    1.98 +using namespace mozilla;
    1.99 +using namespace mozilla::dom;
   1.100 +
   1.101 +#define kInsertCookie  "_moz_Insert Here_moz_"
   1.102 +
   1.103 +// some little helpers
   1.104 +static bool FindIntegerAfterString(const char *aLeadingString, 
   1.105 +                                     nsCString &aCStr, int32_t &foundNumber);
   1.106 +static nsresult RemoveFragComments(nsCString &theStr);
   1.107 +static void RemoveBodyAndHead(nsIDOMNode *aNode);
   1.108 +static nsresult FindTargetNode(nsIDOMNode *aStart, nsCOMPtr<nsIDOMNode> &aResult);
   1.109 +
   1.110 +static nsCOMPtr<nsIDOMNode> GetListParent(nsIDOMNode* aNode)
   1.111 +{
   1.112 +  NS_ENSURE_TRUE(aNode, nullptr);
   1.113 +  nsCOMPtr<nsIDOMNode> parent, tmp;
   1.114 +  aNode->GetParentNode(getter_AddRefs(parent));
   1.115 +  while (parent)
   1.116 +  {
   1.117 +    if (nsHTMLEditUtils::IsList(parent)) {
   1.118 +      return parent;
   1.119 +    }
   1.120 +    parent->GetParentNode(getter_AddRefs(tmp));
   1.121 +    parent = tmp;
   1.122 +  }
   1.123 +  return nullptr;
   1.124 +}
   1.125 +
   1.126 +static nsCOMPtr<nsIDOMNode> GetTableParent(nsIDOMNode* aNode)
   1.127 +{
   1.128 +  NS_ENSURE_TRUE(aNode, nullptr);
   1.129 +  nsCOMPtr<nsIDOMNode> parent, tmp;
   1.130 +  aNode->GetParentNode(getter_AddRefs(parent));
   1.131 +  while (parent)
   1.132 +  {
   1.133 +    if (nsHTMLEditUtils::IsTable(parent)) {
   1.134 +      return parent;
   1.135 +    }
   1.136 +    parent->GetParentNode(getter_AddRefs(tmp));
   1.137 +    parent = tmp;
   1.138 +  }
   1.139 +  return nullptr;
   1.140 +}
   1.141 +
   1.142 +
   1.143 +NS_IMETHODIMP nsHTMLEditor::LoadHTML(const nsAString & aInputString)
   1.144 +{
   1.145 +  NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
   1.146 +
   1.147 +  // force IME commit; set up rules sniffing and batching
   1.148 +  ForceCompositionEnd();
   1.149 +  nsAutoEditBatch beginBatching(this);
   1.150 +  nsAutoRules beginRulesSniffing(this, EditAction::loadHTML, nsIEditor::eNext);
   1.151 +
   1.152 +  // Get selection
   1.153 +  nsRefPtr<Selection> selection = GetSelection();
   1.154 +  NS_ENSURE_STATE(selection);
   1.155 +
   1.156 +  nsTextRulesInfo ruleInfo(EditAction::loadHTML);
   1.157 +  bool cancel, handled;
   1.158 +  // Protect the edit rules object from dying
   1.159 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
   1.160 +  nsresult rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   1.161 +  NS_ENSURE_SUCCESS(rv, rv);
   1.162 +  if (cancel) {
   1.163 +    return NS_OK; // rules canceled the operation
   1.164 +  }
   1.165 +
   1.166 +  if (!handled)
   1.167 +  {
   1.168 +    // Delete Selection, but only if it isn't collapsed, see bug #106269
   1.169 +    if (!selection->Collapsed()) {
   1.170 +      rv = DeleteSelection(eNone, eStrip);
   1.171 +      NS_ENSURE_SUCCESS(rv, rv);
   1.172 +    }
   1.173 +
   1.174 +    // Get the first range in the selection, for context:
   1.175 +    nsCOMPtr<nsIDOMRange> range;
   1.176 +    rv = selection->GetRangeAt(0, getter_AddRefs(range));
   1.177 +    NS_ENSURE_SUCCESS(rv, rv);
   1.178 +    NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
   1.179 +
   1.180 +    // create fragment for pasted html
   1.181 +    nsCOMPtr<nsIDOMDocumentFragment> docfrag;
   1.182 +    {
   1.183 +      rv = range->CreateContextualFragment(aInputString, getter_AddRefs(docfrag));
   1.184 +      NS_ENSURE_SUCCESS(rv, rv);
   1.185 +    }
   1.186 +    // put the fragment into the document
   1.187 +    nsCOMPtr<nsIDOMNode> parent, junk;
   1.188 +    rv = range->GetStartContainer(getter_AddRefs(parent));
   1.189 +    NS_ENSURE_SUCCESS(rv, rv);
   1.190 +    NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
   1.191 +    int32_t childOffset;
   1.192 +    rv = range->GetStartOffset(&childOffset);
   1.193 +    NS_ENSURE_SUCCESS(rv, rv);
   1.194 +
   1.195 +    nsCOMPtr<nsIDOMNode> nodeToInsert;
   1.196 +    docfrag->GetFirstChild(getter_AddRefs(nodeToInsert));
   1.197 +    while (nodeToInsert)
   1.198 +    {
   1.199 +      rv = InsertNode(nodeToInsert, parent, childOffset++);
   1.200 +      NS_ENSURE_SUCCESS(rv, rv);
   1.201 +      docfrag->GetFirstChild(getter_AddRefs(nodeToInsert));
   1.202 +    }
   1.203 +  }
   1.204 +
   1.205 +  return mRules->DidDoAction(selection, &ruleInfo, rv);
   1.206 +}
   1.207 +
   1.208 +
   1.209 +NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsAString & aInString)
   1.210 +{
   1.211 +  const nsAFlatString& empty = EmptyString();
   1.212 +
   1.213 +  return InsertHTMLWithContext(aInString, empty, empty, empty,
   1.214 +                               nullptr,  nullptr, 0, true);
   1.215 +}
   1.216 +
   1.217 +
   1.218 +nsresult
   1.219 +nsHTMLEditor::InsertHTMLWithContext(const nsAString & aInputString,
   1.220 +                                    const nsAString & aContextStr,
   1.221 +                                    const nsAString & aInfoStr,
   1.222 +                                    const nsAString & aFlavor,
   1.223 +                                    nsIDOMDocument *aSourceDoc,
   1.224 +                                    nsIDOMNode *aDestNode,
   1.225 +                                    int32_t aDestOffset,
   1.226 +                                    bool aDeleteSelection)
   1.227 +{
   1.228 +  return DoInsertHTMLWithContext(aInputString, aContextStr, aInfoStr,
   1.229 +      aFlavor, aSourceDoc, aDestNode, aDestOffset, aDeleteSelection,
   1.230 +      /* trusted input */ true, /* clear style */ false);
   1.231 +}
   1.232 +
   1.233 +nsresult
   1.234 +nsHTMLEditor::DoInsertHTMLWithContext(const nsAString & aInputString,
   1.235 +                                      const nsAString & aContextStr,
   1.236 +                                      const nsAString & aInfoStr,
   1.237 +                                      const nsAString & aFlavor,
   1.238 +                                      nsIDOMDocument *aSourceDoc,
   1.239 +                                      nsIDOMNode *aDestNode,
   1.240 +                                      int32_t aDestOffset,
   1.241 +                                      bool aDeleteSelection,
   1.242 +                                      bool aTrustedInput,
   1.243 +                                      bool aClearStyle)
   1.244 +{
   1.245 +  NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
   1.246 +
   1.247 +  // Prevent the edit rules object from dying
   1.248 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
   1.249 +
   1.250 +  // force IME commit; set up rules sniffing and batching
   1.251 +  ForceCompositionEnd();
   1.252 +  nsAutoEditBatch beginBatching(this);
   1.253 +  nsAutoRules beginRulesSniffing(this, EditAction::htmlPaste, nsIEditor::eNext);
   1.254 +
   1.255 +  // Get selection
   1.256 +  nsRefPtr<Selection> selection = GetSelection();
   1.257 +  NS_ENSURE_STATE(selection);
   1.258 +
   1.259 +  // create a dom document fragment that represents the structure to paste
   1.260 +  nsCOMPtr<nsIDOMNode> fragmentAsNode, streamStartParent, streamEndParent;
   1.261 +  int32_t streamStartOffset = 0, streamEndOffset = 0;
   1.262 +
   1.263 +  nsresult rv = CreateDOMFragmentFromPaste(aInputString, aContextStr, aInfoStr,
   1.264 +                                           address_of(fragmentAsNode),
   1.265 +                                           address_of(streamStartParent),
   1.266 +                                           address_of(streamEndParent),
   1.267 +                                           &streamStartOffset,
   1.268 +                                           &streamEndOffset,
   1.269 +                                           aTrustedInput);
   1.270 +  NS_ENSURE_SUCCESS(rv, rv);
   1.271 +
   1.272 +  nsCOMPtr<nsIDOMNode> targetNode, tempNode;
   1.273 +  int32_t targetOffset=0;
   1.274 +
   1.275 +  if (!aDestNode)
   1.276 +  {
   1.277 +    // if caller didn't provide the destination/target node,
   1.278 +    // fetch the paste insertion point from our selection
   1.279 +    rv = GetStartNodeAndOffset(selection, getter_AddRefs(targetNode), &targetOffset);
   1.280 +    NS_ENSURE_SUCCESS(rv, rv);
   1.281 +    if (!targetNode || !IsEditable(targetNode)) {
   1.282 +      return NS_ERROR_FAILURE;
   1.283 +    }
   1.284 +  }
   1.285 +  else
   1.286 +  {
   1.287 +    targetNode = aDestNode;
   1.288 +    targetOffset = aDestOffset;
   1.289 +  }
   1.290 +
   1.291 +  bool doContinue = true;
   1.292 +
   1.293 +  rv = DoContentFilterCallback(aFlavor, aSourceDoc, aDeleteSelection,
   1.294 +                               (nsIDOMNode **)address_of(fragmentAsNode),
   1.295 +                               (nsIDOMNode **)address_of(streamStartParent),
   1.296 +                               &streamStartOffset,
   1.297 +                               (nsIDOMNode **)address_of(streamEndParent),
   1.298 +                               &streamEndOffset,
   1.299 +                               (nsIDOMNode **)address_of(targetNode),
   1.300 +                               &targetOffset, &doContinue);
   1.301 +
   1.302 +  NS_ENSURE_SUCCESS(rv, rv);
   1.303 +  NS_ENSURE_TRUE(doContinue, NS_OK);
   1.304 +
   1.305 +  // if we have a destination / target node, we want to insert there
   1.306 +  // rather than in place of the selection
   1.307 +  // ignore aDeleteSelection here if no aDestNode since deletion will
   1.308 +  // also occur later; this block is intended to cover the various
   1.309 +  // scenarios where we are dropping in an editor (and may want to delete
   1.310 +  // the selection before collapsing the selection in the new destination)
   1.311 +  if (aDestNode)
   1.312 +  {
   1.313 +    if (aDeleteSelection)
   1.314 +    {
   1.315 +      // Use an auto tracker so that our drop point is correctly
   1.316 +      // positioned after the delete.
   1.317 +      nsAutoTrackDOMPoint tracker(mRangeUpdater, &targetNode, &targetOffset);
   1.318 +      rv = DeleteSelection(eNone, eStrip);
   1.319 +      NS_ENSURE_SUCCESS(rv, rv);
   1.320 +    }
   1.321 +
   1.322 +    rv = selection->Collapse(targetNode, targetOffset);
   1.323 +    NS_ENSURE_SUCCESS(rv, rv);
   1.324 +  }
   1.325 +
   1.326 +  // we need to recalculate various things based on potentially new offsets
   1.327 +  // this is work to be completed at a later date (probably by jfrancis)
   1.328 +
   1.329 +  // make a list of what nodes in docFrag we need to move
   1.330 +  nsCOMArray<nsIDOMNode> nodeList;
   1.331 +  rv = CreateListOfNodesToPaste(fragmentAsNode, nodeList,
   1.332 +                                streamStartParent, streamStartOffset,
   1.333 +                                streamEndParent, streamEndOffset);
   1.334 +  NS_ENSURE_SUCCESS(rv, rv);
   1.335 +
   1.336 +  if (nodeList.Count() == 0) {
   1.337 +    return NS_OK;
   1.338 +  }
   1.339 +
   1.340 +  // Are there any table elements in the list?
   1.341 +  // node and offset for insertion
   1.342 +  nsCOMPtr<nsIDOMNode> parentNode;
   1.343 +  int32_t offsetOfNewNode;
   1.344 +
   1.345 +  // check for table cell selection mode
   1.346 +  bool cellSelectionMode = false;
   1.347 +  nsCOMPtr<nsIDOMElement> cell;
   1.348 +  rv = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
   1.349 +  if (NS_SUCCEEDED(rv) && cell)
   1.350 +  {
   1.351 +    cellSelectionMode = true;
   1.352 +  }
   1.353 +
   1.354 +  if (cellSelectionMode)
   1.355 +  {
   1.356 +    // do we have table content to paste?  If so, we want to delete
   1.357 +    // the selected table cells and replace with new table elements;
   1.358 +    // but if not we want to delete _contents_ of cells and replace
   1.359 +    // with non-table elements.  Use cellSelectionMode bool to 
   1.360 +    // indicate results.
   1.361 +    nsIDOMNode* firstNode = nodeList[0];
   1.362 +    if (!nsHTMLEditUtils::IsTableElement(firstNode))
   1.363 +      cellSelectionMode = false;
   1.364 +  }
   1.365 +
   1.366 +  if (!cellSelectionMode)
   1.367 +  {
   1.368 +    rv = DeleteSelectionAndPrepareToCreateNode();
   1.369 +    NS_ENSURE_SUCCESS(rv, rv);
   1.370 +
   1.371 +    if (aClearStyle) {
   1.372 +      // pasting does not inherit local inline styles
   1.373 +      nsCOMPtr<nsIDOMNode> tmpNode =
   1.374 +        do_QueryInterface(selection->GetAnchorNode());
   1.375 +      int32_t tmpOffset = static_cast<int32_t>(selection->AnchorOffset());
   1.376 +      rv = ClearStyle(address_of(tmpNode), &tmpOffset, nullptr, nullptr);
   1.377 +      NS_ENSURE_SUCCESS(rv, rv);
   1.378 +    }
   1.379 +  }
   1.380 +  else
   1.381 +  {
   1.382 +    // delete whole cells: we will replace with new table content
   1.383 +    { // Braces for artificial block to scope nsAutoSelectionReset.
   1.384 +      // Save current selection since DeleteTableCell perturbs it
   1.385 +      nsAutoSelectionReset selectionResetter(selection, this);
   1.386 +      rv = DeleteTableCell(1);
   1.387 +      NS_ENSURE_SUCCESS(rv, rv);
   1.388 +    }
   1.389 +    // collapse selection to beginning of deleted table content
   1.390 +    selection->CollapseToStart();
   1.391 +  }
   1.392 +
   1.393 +  // give rules a chance to handle or cancel
   1.394 +  nsTextRulesInfo ruleInfo(EditAction::insertElement);
   1.395 +  bool cancel, handled;
   1.396 +  rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   1.397 +  NS_ENSURE_SUCCESS(rv, rv);
   1.398 +  if (cancel) {
   1.399 +    return NS_OK; // rules canceled the operation
   1.400 +  }
   1.401 +
   1.402 +  if (!handled)
   1.403 +  {
   1.404 +    // The rules code (WillDoAction above) might have changed the selection.
   1.405 +    // refresh our memory...
   1.406 +    rv = GetStartNodeAndOffset(selection, getter_AddRefs(parentNode), &offsetOfNewNode);
   1.407 +    NS_ENSURE_SUCCESS(rv, rv);
   1.408 +    NS_ENSURE_TRUE(parentNode, NS_ERROR_FAILURE);
   1.409 +
   1.410 +    // Adjust position based on the first node we are going to insert.
   1.411 +    NormalizeEOLInsertPosition(nodeList[0], address_of(parentNode), &offsetOfNewNode);
   1.412 +
   1.413 +    // if there are any invisible br's after our insertion point, remove them.
   1.414 +    // this is because if there is a br at end of what we paste, it will make
   1.415 +    // the invisible br visible.
   1.416 +    nsWSRunObject wsObj(this, parentNode, offsetOfNewNode);
   1.417 +    if (nsTextEditUtils::IsBreak(wsObj.mEndReasonNode) && 
   1.418 +        !IsVisBreak(wsObj.mEndReasonNode) )
   1.419 +    {
   1.420 +      rv = DeleteNode(wsObj.mEndReasonNode);
   1.421 +      NS_ENSURE_SUCCESS(rv, rv);
   1.422 +    }
   1.423 +
   1.424 +    // Remember if we are in a link.
   1.425 +    bool bStartedInLink = IsInLink(parentNode);
   1.426 +
   1.427 +    // Are we in a text node? If so, split it.
   1.428 +    if (IsTextNode(parentNode))
   1.429 +    {
   1.430 +      nsCOMPtr<nsIDOMNode> temp;
   1.431 +      rv = SplitNodeDeep(parentNode, parentNode, offsetOfNewNode, &offsetOfNewNode);
   1.432 +      NS_ENSURE_SUCCESS(rv, rv);
   1.433 +      rv = parentNode->GetParentNode(getter_AddRefs(temp));
   1.434 +      NS_ENSURE_SUCCESS(rv, rv);
   1.435 +      parentNode = temp;
   1.436 +    }
   1.437 +
   1.438 +    // build up list of parents of first node in list that are either
   1.439 +    // lists or tables.  First examine front of paste node list.
   1.440 +    nsCOMArray<nsIDOMNode> startListAndTableArray;
   1.441 +    rv = GetListAndTableParents(false, nodeList, startListAndTableArray);
   1.442 +    NS_ENSURE_SUCCESS(rv, rv);
   1.443 +
   1.444 +    // remember number of lists and tables above us
   1.445 +    int32_t highWaterMark = -1;
   1.446 +    if (startListAndTableArray.Count() > 0)
   1.447 +    {
   1.448 +      rv = DiscoverPartialListsAndTables(nodeList, startListAndTableArray, &highWaterMark);
   1.449 +      NS_ENSURE_SUCCESS(rv, rv);
   1.450 +    }
   1.451 +
   1.452 +    // if we have pieces of tables or lists to be inserted, let's force the paste 
   1.453 +    // to deal with table elements right away, so that it doesn't orphan some 
   1.454 +    // table or list contents outside the table or list.
   1.455 +    if (highWaterMark >= 0)
   1.456 +    {
   1.457 +      rv = ReplaceOrphanedStructure(false, nodeList, startListAndTableArray, highWaterMark);
   1.458 +      NS_ENSURE_SUCCESS(rv, rv);
   1.459 +    }
   1.460 +
   1.461 +    // Now go through the same process again for the end of the paste node list.
   1.462 +    nsCOMArray<nsIDOMNode> endListAndTableArray;
   1.463 +    rv = GetListAndTableParents(true, nodeList, endListAndTableArray);
   1.464 +    NS_ENSURE_SUCCESS(rv, rv);
   1.465 +    highWaterMark = -1;
   1.466 +
   1.467 +    // remember number of lists and tables above us
   1.468 +    if (endListAndTableArray.Count() > 0)
   1.469 +    {
   1.470 +      rv = DiscoverPartialListsAndTables(nodeList, endListAndTableArray, &highWaterMark);
   1.471 +      NS_ENSURE_SUCCESS(rv, rv);
   1.472 +    }
   1.473 +
   1.474 +    // don't orphan partial list or table structure
   1.475 +    if (highWaterMark >= 0)
   1.476 +    {
   1.477 +      rv = ReplaceOrphanedStructure(true, nodeList, endListAndTableArray, highWaterMark);
   1.478 +      NS_ENSURE_SUCCESS(rv, rv);
   1.479 +    }
   1.480 +
   1.481 +    // Loop over the node list and paste the nodes:
   1.482 +    nsCOMPtr<nsIDOMNode> parentBlock, lastInsertNode, insertedContextParent;
   1.483 +    int32_t listCount = nodeList.Count();
   1.484 +    int32_t j;
   1.485 +    if (IsBlockNode(parentNode))
   1.486 +      parentBlock = parentNode;
   1.487 +    else
   1.488 +      parentBlock = GetBlockNodeParent(parentNode);
   1.489 +
   1.490 +    for (j=0; j<listCount; j++)
   1.491 +    {
   1.492 +      bool bDidInsert = false;
   1.493 +      nsCOMPtr<nsIDOMNode> curNode = nodeList[j];
   1.494 +
   1.495 +      NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE);
   1.496 +      NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE);
   1.497 +      NS_ENSURE_TRUE(!nsTextEditUtils::IsBody(curNode), NS_ERROR_FAILURE);
   1.498 +
   1.499 +      if (insertedContextParent)
   1.500 +      {
   1.501 +        // if we had to insert something higher up in the paste hierarchy, we want to 
   1.502 +        // skip any further paste nodes that descend from that.  Else we will paste twice.
   1.503 +        if (nsEditorUtils::IsDescendantOf(curNode, insertedContextParent))
   1.504 +          continue;
   1.505 +      }
   1.506 +
   1.507 +      // give the user a hand on table element insertion.  if they have
   1.508 +      // a table or table row on the clipboard, and are trying to insert
   1.509 +      // into a table or table row, insert the appropriate children instead.
   1.510 +      if (  (nsHTMLEditUtils::IsTableRow(curNode) && nsHTMLEditUtils::IsTableRow(parentNode))
   1.511 +         && (nsHTMLEditUtils::IsTable(curNode)    || nsHTMLEditUtils::IsTable(parentNode)) )
   1.512 +      {
   1.513 +        nsCOMPtr<nsIDOMNode> child;
   1.514 +        curNode->GetFirstChild(getter_AddRefs(child));
   1.515 +        while (child)
   1.516 +        {
   1.517 +          rv = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, true);
   1.518 +          if (NS_FAILED(rv))
   1.519 +            break;
   1.520 +
   1.521 +          bDidInsert = true;
   1.522 +          lastInsertNode = child;
   1.523 +          offsetOfNewNode++;
   1.524 +
   1.525 +          curNode->GetFirstChild(getter_AddRefs(child));
   1.526 +        }
   1.527 +      }
   1.528 +      // give the user a hand on list insertion.  if they have
   1.529 +      // a list on the clipboard, and are trying to insert
   1.530 +      // into a list or list item, insert the appropriate children instead,
   1.531 +      // ie, merge the lists instead of pasting in a sublist.
   1.532 +      else if (nsHTMLEditUtils::IsList(curNode) && 
   1.533 +              (nsHTMLEditUtils::IsList(parentNode)  || nsHTMLEditUtils::IsListItem(parentNode)) )
   1.534 +      {
   1.535 +        nsCOMPtr<nsIDOMNode> child, tmp;
   1.536 +        curNode->GetFirstChild(getter_AddRefs(child));
   1.537 +        while (child)
   1.538 +        {
   1.539 +          if (nsHTMLEditUtils::IsListItem(child) || nsHTMLEditUtils::IsList(child))
   1.540 +          {
   1.541 +            // Check if we are pasting into empty list item. If so
   1.542 +            // delete it and paste into parent list instead.
   1.543 +            if (nsHTMLEditUtils::IsListItem(parentNode))
   1.544 +            {
   1.545 +              bool isEmpty;
   1.546 +              rv = IsEmptyNode(parentNode, &isEmpty, true);
   1.547 +              if (NS_SUCCEEDED(rv) && isEmpty)
   1.548 +              {
   1.549 +                int32_t newOffset;
   1.550 +                nsCOMPtr<nsIDOMNode> listNode = GetNodeLocation(parentNode, &newOffset);
   1.551 +                if (listNode)
   1.552 +                {
   1.553 +                  DeleteNode(parentNode);
   1.554 +                  parentNode = listNode;
   1.555 +                  offsetOfNewNode = newOffset;
   1.556 +                }
   1.557 +              }
   1.558 +            }
   1.559 +            rv = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, true);
   1.560 +            if (NS_FAILED(rv))
   1.561 +              break;
   1.562 +
   1.563 +            bDidInsert = true;
   1.564 +            lastInsertNode = child;
   1.565 +            offsetOfNewNode++;
   1.566 +          }
   1.567 +          else
   1.568 +          {
   1.569 +            curNode->RemoveChild(child, getter_AddRefs(tmp));
   1.570 +          }
   1.571 +          curNode->GetFirstChild(getter_AddRefs(child));
   1.572 +        }
   1.573 +
   1.574 +      }
   1.575 +      // Check for pre's going into pre's.
   1.576 +      else if (nsHTMLEditUtils::IsPre(parentBlock) && nsHTMLEditUtils::IsPre(curNode))
   1.577 +      {
   1.578 +        nsCOMPtr<nsIDOMNode> child, tmp;
   1.579 +        curNode->GetFirstChild(getter_AddRefs(child));
   1.580 +        while (child)
   1.581 +        {
   1.582 +          rv = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, true);
   1.583 +          if (NS_FAILED(rv))
   1.584 +            break;
   1.585 +
   1.586 +          bDidInsert = true;
   1.587 +          lastInsertNode = child;
   1.588 +          offsetOfNewNode++;
   1.589 +
   1.590 +          curNode->GetFirstChild(getter_AddRefs(child));
   1.591 +        }
   1.592 +      }
   1.593 +
   1.594 +      if (!bDidInsert || NS_FAILED(rv))
   1.595 +      {
   1.596 +        // try to insert
   1.597 +        rv = InsertNodeAtPoint(curNode, address_of(parentNode), &offsetOfNewNode, true);
   1.598 +        if (NS_SUCCEEDED(rv))
   1.599 +        {
   1.600 +          bDidInsert = true;
   1.601 +          lastInsertNode = curNode;
   1.602 +        }
   1.603 +
   1.604 +        // Assume failure means no legal parent in the document hierarchy,
   1.605 +        // try again with the parent of curNode in the paste hierarchy.
   1.606 +        nsCOMPtr<nsIDOMNode> parent;
   1.607 +        while (NS_FAILED(rv) && curNode)
   1.608 +        {
   1.609 +          curNode->GetParentNode(getter_AddRefs(parent));
   1.610 +          if (parent && !nsTextEditUtils::IsBody(parent))
   1.611 +          {
   1.612 +            rv = InsertNodeAtPoint(parent, address_of(parentNode), &offsetOfNewNode, true);
   1.613 +            if (NS_SUCCEEDED(rv))
   1.614 +            {
   1.615 +              bDidInsert = true;
   1.616 +              insertedContextParent = parent;
   1.617 +              lastInsertNode = GetChildAt(parentNode, offsetOfNewNode);
   1.618 +            }
   1.619 +          }
   1.620 +          curNode = parent;
   1.621 +        }
   1.622 +      }
   1.623 +      if (lastInsertNode)
   1.624 +      {
   1.625 +        parentNode = GetNodeLocation(lastInsertNode, &offsetOfNewNode);
   1.626 +        offsetOfNewNode++;
   1.627 +      }
   1.628 +    }
   1.629 +
   1.630 +    // Now collapse the selection to the end of what we just inserted:
   1.631 +    if (lastInsertNode) 
   1.632 +    {
   1.633 +      // set selection to the end of what we just pasted.
   1.634 +      nsCOMPtr<nsIDOMNode> selNode, tmp, visNode, highTable;
   1.635 +      int32_t selOffset;
   1.636 +
   1.637 +      // but don't cross tables
   1.638 +      if (!nsHTMLEditUtils::IsTable(lastInsertNode))
   1.639 +      {
   1.640 +        rv = GetLastEditableLeaf(lastInsertNode, address_of(selNode));
   1.641 +        NS_ENSURE_SUCCESS(rv, rv);
   1.642 +        tmp = selNode;
   1.643 +        while (tmp && (tmp != lastInsertNode))
   1.644 +        {
   1.645 +          if (nsHTMLEditUtils::IsTable(tmp))
   1.646 +            highTable = tmp;
   1.647 +          nsCOMPtr<nsIDOMNode> parent = tmp;
   1.648 +          tmp->GetParentNode(getter_AddRefs(parent));
   1.649 +          tmp = parent;
   1.650 +        }
   1.651 +        if (highTable)
   1.652 +          selNode = highTable;
   1.653 +      }
   1.654 +      if (!selNode)
   1.655 +        selNode = lastInsertNode;
   1.656 +      if (IsTextNode(selNode) || (IsContainer(selNode) && !nsHTMLEditUtils::IsTable(selNode)))
   1.657 +      {
   1.658 +        rv = GetLengthOfDOMNode(selNode, (uint32_t&)selOffset);
   1.659 +        NS_ENSURE_SUCCESS(rv, rv);
   1.660 +      }
   1.661 +      else // we need to find a container for selection.  Look up.
   1.662 +      {
   1.663 +        tmp = selNode;
   1.664 +        selNode = GetNodeLocation(tmp, &selOffset);
   1.665 +        ++selOffset;  // want to be *after* last leaf node in paste
   1.666 +      }
   1.667 +
   1.668 +      // make sure we don't end up with selection collapsed after an invisible break node
   1.669 +      nsWSRunObject wsRunObj(this, selNode, selOffset);
   1.670 +      int32_t outVisOffset=0;
   1.671 +      WSType visType;
   1.672 +      wsRunObj.PriorVisibleNode(selNode, selOffset, address_of(visNode),
   1.673 +                                &outVisOffset, &visType);
   1.674 +      if (visType == WSType::br) {
   1.675 +        // we are after a break.  Is it visible?  Despite the name, 
   1.676 +        // PriorVisibleNode does not make that determination for breaks.
   1.677 +        // It also may not return the break in visNode.  We have to pull it
   1.678 +        // out of the nsWSRunObject's state.
   1.679 +        if (!IsVisBreak(wsRunObj.mStartReasonNode))
   1.680 +        {
   1.681 +          // don't leave selection past an invisible break;
   1.682 +          // reset {selNode,selOffset} to point before break
   1.683 +          selNode = GetNodeLocation(wsRunObj.mStartReasonNode, &selOffset);
   1.684 +          // we want to be inside any inline style prior to break
   1.685 +          nsWSRunObject wsRunObj(this, selNode, selOffset);
   1.686 +          wsRunObj.PriorVisibleNode(selNode, selOffset, address_of(visNode),
   1.687 +                                    &outVisOffset, &visType);
   1.688 +          if (visType == WSType::text || visType == WSType::normalWS) {
   1.689 +            selNode = visNode;
   1.690 +            selOffset = outVisOffset;  // PriorVisibleNode already set offset to _after_ the text or ws
   1.691 +          } else if (visType == WSType::special) {
   1.692 +            // prior visible thing is an image or some other non-text thingy.  
   1.693 +            // We want to be right after it.
   1.694 +            selNode = GetNodeLocation(wsRunObj.mStartReasonNode, &selOffset);
   1.695 +            ++selOffset;
   1.696 +          }
   1.697 +        }
   1.698 +      }
   1.699 +      selection->Collapse(selNode, selOffset);
   1.700 +
   1.701 +      // if we just pasted a link, discontinue link style
   1.702 +      nsCOMPtr<nsIDOMNode> link;
   1.703 +      if (!bStartedInLink && IsInLink(selNode, address_of(link)))
   1.704 +      {
   1.705 +        // so, if we just pasted a link, I split it.  Why do that instead of just
   1.706 +        // nudging selection point beyond it?  Because it might have ended in a BR
   1.707 +        // that is not visible.  If so, the code above just placed selection
   1.708 +        // inside that.  So I split it instead.
   1.709 +        nsCOMPtr<nsIDOMNode> leftLink;
   1.710 +        int32_t linkOffset;
   1.711 +        rv = SplitNodeDeep(link, selNode, selOffset, &linkOffset, true, address_of(leftLink));
   1.712 +        NS_ENSURE_SUCCESS(rv, rv);
   1.713 +        selNode = GetNodeLocation(leftLink, &selOffset);
   1.714 +        selection->Collapse(selNode, selOffset+1);
   1.715 +      }
   1.716 +    }
   1.717 +  }
   1.718 +
   1.719 +  return mRules->DidDoAction(selection, &ruleInfo, rv);
   1.720 +}
   1.721 +
   1.722 +nsresult
   1.723 +nsHTMLEditor::AddInsertionListener(nsIContentFilter *aListener)
   1.724 +{
   1.725 +  NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
   1.726 +
   1.727 +  // don't let a listener be added more than once
   1.728 +  if (mContentFilters.IndexOfObject(aListener) == -1)
   1.729 +  {
   1.730 +    NS_ENSURE_TRUE(mContentFilters.AppendObject(aListener), NS_ERROR_FAILURE);
   1.731 +  }
   1.732 +
   1.733 +  return NS_OK;
   1.734 +}
   1.735 +
   1.736 +nsresult
   1.737 +nsHTMLEditor::RemoveInsertionListener(nsIContentFilter *aListener)
   1.738 +{
   1.739 +  NS_ENSURE_TRUE(aListener, NS_ERROR_FAILURE);
   1.740 +
   1.741 +  NS_ENSURE_TRUE(mContentFilters.RemoveObject(aListener), NS_ERROR_FAILURE);
   1.742 +
   1.743 +  return NS_OK;
   1.744 +}
   1.745 + 
   1.746 +nsresult
   1.747 +nsHTMLEditor::DoContentFilterCallback(const nsAString &aFlavor, 
   1.748 +                                      nsIDOMDocument *sourceDoc,
   1.749 +                                      bool aWillDeleteSelection,
   1.750 +                                      nsIDOMNode **aFragmentAsNode, 
   1.751 +                                      nsIDOMNode **aFragStartNode, 
   1.752 +                                      int32_t *aFragStartOffset,
   1.753 +                                      nsIDOMNode **aFragEndNode, 
   1.754 +                                      int32_t *aFragEndOffset,
   1.755 +                                      nsIDOMNode **aTargetNode,
   1.756 +                                      int32_t *aTargetOffset,
   1.757 +                                      bool *aDoContinue)
   1.758 +{
   1.759 +  *aDoContinue = true;
   1.760 +
   1.761 +  int32_t i;
   1.762 +  nsIContentFilter *listener;
   1.763 +  for (i=0; i < mContentFilters.Count() && *aDoContinue; i++)
   1.764 +  {
   1.765 +    listener = (nsIContentFilter *)mContentFilters[i];
   1.766 +    if (listener)
   1.767 +      listener->NotifyOfInsertion(aFlavor, nullptr, sourceDoc,
   1.768 +                                  aWillDeleteSelection, aFragmentAsNode,
   1.769 +                                  aFragStartNode, aFragStartOffset, 
   1.770 +                                  aFragEndNode, aFragEndOffset,
   1.771 +                                  aTargetNode, aTargetOffset, aDoContinue);
   1.772 +  }
   1.773 +
   1.774 +  return NS_OK;
   1.775 +}
   1.776 +
   1.777 +bool
   1.778 +nsHTMLEditor::IsInLink(nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *outLink)
   1.779 +{
   1.780 +  NS_ENSURE_TRUE(aNode, false);
   1.781 +  if (outLink)
   1.782 +    *outLink = nullptr;
   1.783 +  nsCOMPtr<nsIDOMNode> tmp, node = aNode;
   1.784 +  while (node)
   1.785 +  {
   1.786 +    if (nsHTMLEditUtils::IsLink(node)) 
   1.787 +    {
   1.788 +      if (outLink)
   1.789 +        *outLink = node;
   1.790 +      return true;
   1.791 +    }
   1.792 +    tmp = node;
   1.793 +    tmp->GetParentNode(getter_AddRefs(node));
   1.794 +  }
   1.795 +  return false;
   1.796 +}
   1.797 +
   1.798 +
   1.799 +nsresult
   1.800 +nsHTMLEditor::StripFormattingNodes(nsIDOMNode *aNode, bool aListOnly)
   1.801 +{
   1.802 +  NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
   1.803 +
   1.804 +  nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
   1.805 +  if (content->TextIsOnlyWhitespace())
   1.806 +  {
   1.807 +    nsCOMPtr<nsIDOMNode> parent, ignored;
   1.808 +    aNode->GetParentNode(getter_AddRefs(parent));
   1.809 +    if (parent)
   1.810 +    {
   1.811 +      if (!aListOnly || nsHTMLEditUtils::IsList(parent)) {
   1.812 +        return parent->RemoveChild(aNode, getter_AddRefs(ignored));
   1.813 +      }
   1.814 +      return NS_OK;
   1.815 +    }
   1.816 +  }
   1.817 +
   1.818 +  if (!nsHTMLEditUtils::IsPre(aNode))
   1.819 +  {
   1.820 +    nsCOMPtr<nsIDOMNode> child;
   1.821 +    aNode->GetLastChild(getter_AddRefs(child));
   1.822 +
   1.823 +    while (child)
   1.824 +    {
   1.825 +      nsCOMPtr<nsIDOMNode> tmp;
   1.826 +      child->GetPreviousSibling(getter_AddRefs(tmp));
   1.827 +      nsresult rv = StripFormattingNodes(child, aListOnly);
   1.828 +      NS_ENSURE_SUCCESS(rv, rv);
   1.829 +      child = tmp;
   1.830 +    }
   1.831 +  }
   1.832 +  return NS_OK;
   1.833 +}
   1.834 +
   1.835 +NS_IMETHODIMP nsHTMLEditor::PrepareTransferable(nsITransferable **transferable)
   1.836 +{
   1.837 +  return NS_OK;
   1.838 +}
   1.839 +
   1.840 +NS_IMETHODIMP nsHTMLEditor::PrepareHTMLTransferable(nsITransferable **aTransferable, 
   1.841 +                                                    bool aHavePrivFlavor)
   1.842 +{
   1.843 +  // Create generic Transferable for getting the data
   1.844 +  nsresult rv = CallCreateInstance("@mozilla.org/widget/transferable;1", aTransferable);
   1.845 +  NS_ENSURE_SUCCESS(rv, rv);
   1.846 +
   1.847 +  // Get the nsITransferable interface for getting the data from the clipboard
   1.848 +  if (aTransferable)
   1.849 +  {
   1.850 +    nsCOMPtr<nsIDocument> destdoc = GetDocument();
   1.851 +    nsILoadContext* loadContext = destdoc ? destdoc->GetLoadContext() : nullptr;
   1.852 +    (*aTransferable)->Init(loadContext);
   1.853 +
   1.854 +    // Create the desired DataFlavor for the type of data
   1.855 +    // we want to get out of the transferable
   1.856 +    // This should only happen in html editors, not plaintext
   1.857 +    if (!IsPlaintextEditor())
   1.858 +    {
   1.859 +      if (!aHavePrivFlavor) 
   1.860 +      {
   1.861 +        (*aTransferable)->AddDataFlavor(kNativeHTMLMime);
   1.862 +      }
   1.863 +      (*aTransferable)->AddDataFlavor(kHTMLMime);
   1.864 +      (*aTransferable)->AddDataFlavor(kFileMime);
   1.865 +
   1.866 +      switch (Preferences::GetInt("clipboard.paste_image_type", 1))
   1.867 +      {
   1.868 +        case 0:  // prefer JPEG over PNG over GIF encoding
   1.869 +          (*aTransferable)->AddDataFlavor(kJPEGImageMime);
   1.870 +          (*aTransferable)->AddDataFlavor(kJPGImageMime);
   1.871 +          (*aTransferable)->AddDataFlavor(kPNGImageMime);
   1.872 +          (*aTransferable)->AddDataFlavor(kGIFImageMime);
   1.873 +          break;
   1.874 +        case 1:  // prefer PNG over JPEG over GIF encoding (default)
   1.875 +        default:
   1.876 +          (*aTransferable)->AddDataFlavor(kPNGImageMime);
   1.877 +          (*aTransferable)->AddDataFlavor(kJPEGImageMime);
   1.878 +          (*aTransferable)->AddDataFlavor(kJPGImageMime);
   1.879 +          (*aTransferable)->AddDataFlavor(kGIFImageMime);
   1.880 +          break;
   1.881 +        case 2:  // prefer GIF over JPEG over PNG encoding
   1.882 +          (*aTransferable)->AddDataFlavor(kGIFImageMime);
   1.883 +          (*aTransferable)->AddDataFlavor(kJPEGImageMime);
   1.884 +          (*aTransferable)->AddDataFlavor(kJPGImageMime);
   1.885 +          (*aTransferable)->AddDataFlavor(kPNGImageMime);
   1.886 +          break;
   1.887 +      }
   1.888 +    }
   1.889 +    (*aTransferable)->AddDataFlavor(kUnicodeMime);
   1.890 +    (*aTransferable)->AddDataFlavor(kMozTextInternal);
   1.891 +  }
   1.892 +  
   1.893 +  return NS_OK;
   1.894 +}
   1.895 +
   1.896 +bool
   1.897 +FindIntegerAfterString(const char *aLeadingString, 
   1.898 +                       nsCString &aCStr, int32_t &foundNumber)
   1.899 +{
   1.900 +  // first obtain offsets from cfhtml str
   1.901 +  int32_t numFront = aCStr.Find(aLeadingString);
   1.902 +  if (numFront == -1)
   1.903 +    return false;
   1.904 +  numFront += strlen(aLeadingString);
   1.905 +
   1.906 +  int32_t numBack = aCStr.FindCharInSet(CRLF, numFront);
   1.907 +  if (numBack == -1)
   1.908 +    return false;
   1.909 +
   1.910 +  nsAutoCString numStr(Substring(aCStr, numFront, numBack-numFront));
   1.911 +  nsresult errorCode;
   1.912 +  foundNumber = numStr.ToInteger(&errorCode);
   1.913 +  return true;
   1.914 +}
   1.915 +
   1.916 +nsresult
   1.917 +RemoveFragComments(nsCString & aStr)
   1.918 +{
   1.919 +  // remove the StartFragment/EndFragment comments from the str, if present
   1.920 +  int32_t startCommentIndx = aStr.Find("<!--StartFragment");
   1.921 +  if (startCommentIndx >= 0)
   1.922 +  {
   1.923 +    int32_t startCommentEnd = aStr.Find("-->", false, startCommentIndx);
   1.924 +    if (startCommentEnd > startCommentIndx)
   1.925 +      aStr.Cut(startCommentIndx, (startCommentEnd+3)-startCommentIndx);
   1.926 +  }
   1.927 +  int32_t endCommentIndx = aStr.Find("<!--EndFragment");
   1.928 +  if (endCommentIndx >= 0)
   1.929 +  {
   1.930 +    int32_t endCommentEnd = aStr.Find("-->", false, endCommentIndx);
   1.931 +    if (endCommentEnd > endCommentIndx)
   1.932 +      aStr.Cut(endCommentIndx, (endCommentEnd+3)-endCommentIndx);
   1.933 +  }
   1.934 +  return NS_OK;
   1.935 +}
   1.936 +
   1.937 +nsresult
   1.938 +nsHTMLEditor::ParseCFHTML(nsCString & aCfhtml, char16_t **aStuffToPaste, char16_t **aCfcontext)
   1.939 +{
   1.940 +  // First obtain offsets from cfhtml str.
   1.941 +  int32_t startHTML, endHTML, startFragment, endFragment;
   1.942 +  if (!FindIntegerAfterString("StartHTML:", aCfhtml, startHTML) || 
   1.943 +      startHTML < -1)
   1.944 +    return NS_ERROR_FAILURE;
   1.945 +  if (!FindIntegerAfterString("EndHTML:", aCfhtml, endHTML) || 
   1.946 +      endHTML < -1)
   1.947 +    return NS_ERROR_FAILURE;
   1.948 +  if (!FindIntegerAfterString("StartFragment:", aCfhtml, startFragment) || 
   1.949 +      startFragment < 0)
   1.950 +    return NS_ERROR_FAILURE;
   1.951 +  if (!FindIntegerAfterString("EndFragment:", aCfhtml, endFragment) || 
   1.952 +      startFragment < 0)
   1.953 +    return NS_ERROR_FAILURE;
   1.954 +
   1.955 +  // The StartHTML and EndHTML markers are allowed to be -1 to include everything.
   1.956 +  //   See Reference: MSDN doc entitled "HTML Clipboard Format"
   1.957 +  //   http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx#unknown_854
   1.958 +  if (startHTML == -1) {
   1.959 +    startHTML = aCfhtml.Find("<!--StartFragment-->");
   1.960 +    if (startHTML == -1)
   1.961 +      return NS_OK;
   1.962 +  }
   1.963 +  if (endHTML == -1) {
   1.964 +    const char endFragmentMarker[] = "<!--EndFragment-->";
   1.965 +    endHTML = aCfhtml.Find(endFragmentMarker);
   1.966 +    if (endHTML == -1)
   1.967 +      return NS_OK;
   1.968 +    endHTML += ArrayLength(endFragmentMarker) - 1;
   1.969 +  }
   1.970 +
   1.971 +  // create context string
   1.972 +  nsAutoCString contextUTF8(Substring(aCfhtml, startHTML, startFragment - startHTML) +
   1.973 +                            NS_LITERAL_CSTRING("<!--" kInsertCookie "-->") +
   1.974 +                            Substring(aCfhtml, endFragment, endHTML - endFragment));
   1.975 +
   1.976 +  // validate startFragment
   1.977 +  // make sure it's not in the middle of a HTML tag
   1.978 +  // see bug #228879 for more details
   1.979 +  int32_t curPos = startFragment;
   1.980 +  while (curPos > startHTML)
   1.981 +  {
   1.982 +      if (aCfhtml[curPos] == '>')
   1.983 +      {
   1.984 +          // working backwards, the first thing we see is the end of a tag
   1.985 +          // so StartFragment is good, so do nothing.
   1.986 +          break;
   1.987 +      }
   1.988 +      else if (aCfhtml[curPos] == '<') 
   1.989 +      {
   1.990 +          // if we are at the start, then we want to see the '<'
   1.991 +          if (curPos != startFragment) 
   1.992 +          {
   1.993 +              // working backwards, the first thing we see is the start of a tag
   1.994 +              // so StartFragment is bad, so we need to update it.
   1.995 +              NS_ERROR("StartFragment byte count in the clipboard looks bad, see bug #228879");
   1.996 +              startFragment = curPos - 1;
   1.997 +          }
   1.998 +          break;
   1.999 +      }
  1.1000 +      else 
  1.1001 +      {
  1.1002 +          curPos--;
  1.1003 +      }
  1.1004 +  }
  1.1005 +
  1.1006 +  // create fragment string
  1.1007 +  nsAutoCString fragmentUTF8(Substring(aCfhtml, startFragment, endFragment-startFragment));
  1.1008 +
  1.1009 +  // remove the StartFragment/EndFragment comments from the fragment, if present
  1.1010 +  RemoveFragComments(fragmentUTF8);
  1.1011 +
  1.1012 +  // remove the StartFragment/EndFragment comments from the context, if present
  1.1013 +  RemoveFragComments(contextUTF8);
  1.1014 +
  1.1015 +  // convert both strings to usc2
  1.1016 +  const nsAFlatString& fragUcs2Str = NS_ConvertUTF8toUTF16(fragmentUTF8);
  1.1017 +  const nsAFlatString& cntxtUcs2Str = NS_ConvertUTF8toUTF16(contextUTF8);
  1.1018 +
  1.1019 +  // translate platform linebreaks for fragment
  1.1020 +  int32_t oldLengthInChars = fragUcs2Str.Length() + 1;  // +1 to include null terminator
  1.1021 +  int32_t newLengthInChars = 0;
  1.1022 +  *aStuffToPaste = nsLinebreakConverter::ConvertUnicharLineBreaks(fragUcs2Str.get(),
  1.1023 +                                                           nsLinebreakConverter::eLinebreakAny, 
  1.1024 +                                                           nsLinebreakConverter::eLinebreakContent, 
  1.1025 +                                                           oldLengthInChars, &newLengthInChars);
  1.1026 +  NS_ENSURE_TRUE(*aStuffToPaste, NS_ERROR_FAILURE);
  1.1027 +
  1.1028 +  // translate platform linebreaks for context
  1.1029 +  oldLengthInChars = cntxtUcs2Str.Length() + 1;  // +1 to include null terminator
  1.1030 +  newLengthInChars = 0;
  1.1031 +  *aCfcontext = nsLinebreakConverter::ConvertUnicharLineBreaks(cntxtUcs2Str.get(),
  1.1032 +                                                           nsLinebreakConverter::eLinebreakAny, 
  1.1033 +                                                           nsLinebreakConverter::eLinebreakContent, 
  1.1034 +                                                           oldLengthInChars, &newLengthInChars);
  1.1035 +  // it's ok for context to be empty.  frag might be whole doc and contain all its context.
  1.1036 +
  1.1037 +  // we're done!
  1.1038 +  return NS_OK;
  1.1039 +}
  1.1040 +
  1.1041 +nsresult nsHTMLEditor::InsertObject(const char* aType, nsISupports* aObject, bool aIsSafe,
  1.1042 +                                    nsIDOMDocument *aSourceDoc,
  1.1043 +                                    nsIDOMNode *aDestinationNode,
  1.1044 +                                    int32_t aDestOffset,
  1.1045 +                                    bool aDoDeleteSelection)
  1.1046 +{
  1.1047 +  nsresult rv;
  1.1048 +
  1.1049 +  const char* type = aType;
  1.1050 +
  1.1051 +  // Check to see if we can insert an image file
  1.1052 +  bool insertAsImage = false;
  1.1053 +  nsCOMPtr<nsIURI> fileURI;
  1.1054 +  if (0 == nsCRT::strcmp(type, kFileMime))
  1.1055 +  {
  1.1056 +    nsCOMPtr<nsIFile> fileObj = do_QueryInterface(aObject);
  1.1057 +    if (fileObj)
  1.1058 +    {
  1.1059 +      rv = NS_NewFileURI(getter_AddRefs(fileURI), fileObj);
  1.1060 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1061 +
  1.1062 +      nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1");
  1.1063 +      NS_ENSURE_TRUE(mime, NS_ERROR_FAILURE);
  1.1064 +      nsAutoCString contentType;
  1.1065 +      rv = mime->GetTypeFromFile(fileObj, contentType);
  1.1066 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1067 +
  1.1068 +      // Accept any image type fed to us
  1.1069 +      if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("image/"))) {
  1.1070 +        insertAsImage = true;
  1.1071 +        type = contentType.get();
  1.1072 +      }
  1.1073 +    }
  1.1074 +  }
  1.1075 +
  1.1076 +  if (0 == nsCRT::strcmp(type, kJPEGImageMime) ||
  1.1077 +      0 == nsCRT::strcmp(type, kJPGImageMime) ||
  1.1078 +      0 == nsCRT::strcmp(type, kPNGImageMime) ||
  1.1079 +      0 == nsCRT::strcmp(type, kGIFImageMime) ||
  1.1080 +      insertAsImage)
  1.1081 +  {
  1.1082 +    nsCOMPtr<nsIInputStream> imageStream;
  1.1083 +    if (insertAsImage) {
  1.1084 +      NS_ASSERTION(fileURI, "The file URI should be retrieved earlier");
  1.1085 +      rv = NS_OpenURI(getter_AddRefs(imageStream), fileURI);
  1.1086 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1087 +    } else {
  1.1088 +      imageStream = do_QueryInterface(aObject);
  1.1089 +      NS_ENSURE_TRUE(imageStream, NS_ERROR_FAILURE);
  1.1090 +    }
  1.1091 +
  1.1092 +    nsCString imageData;
  1.1093 +    rv = NS_ConsumeStream(imageStream, UINT32_MAX, imageData);
  1.1094 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1095 +
  1.1096 +    rv = imageStream->Close();
  1.1097 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1098 +
  1.1099 +    nsAutoCString data64;
  1.1100 +    rv = Base64Encode(imageData, data64);
  1.1101 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1102 +
  1.1103 +    nsAutoString stuffToPaste;
  1.1104 +    stuffToPaste.AssignLiteral("<IMG src=\"data:");
  1.1105 +    AppendUTF8toUTF16(type, stuffToPaste);
  1.1106 +    stuffToPaste.AppendLiteral(";base64,");
  1.1107 +    AppendUTF8toUTF16(data64, stuffToPaste);
  1.1108 +    stuffToPaste.AppendLiteral("\" alt=\"\" >");
  1.1109 +    nsAutoEditBatch beginBatching(this);
  1.1110 +    rv = DoInsertHTMLWithContext(stuffToPaste, EmptyString(), EmptyString(), 
  1.1111 +                                 NS_LITERAL_STRING(kFileMime),
  1.1112 +                                 aSourceDoc,
  1.1113 +                                 aDestinationNode, aDestOffset,
  1.1114 +                                 aDoDeleteSelection,
  1.1115 +                                 aIsSafe);
  1.1116 +  }
  1.1117 +
  1.1118 +  return NS_OK;
  1.1119 +}
  1.1120 +
  1.1121 +NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable, 
  1.1122 +                                                   nsIDOMDocument *aSourceDoc,
  1.1123 +                                                   const nsAString & aContextStr,
  1.1124 +                                                   const nsAString & aInfoStr,
  1.1125 +                                                   nsIDOMNode *aDestinationNode,
  1.1126 +                                                   int32_t aDestOffset,
  1.1127 +                                                   bool aDoDeleteSelection)
  1.1128 +{
  1.1129 +  nsresult rv = NS_OK;
  1.1130 +  nsXPIDLCString bestFlavor;
  1.1131 +  nsCOMPtr<nsISupports> genericDataObj;
  1.1132 +  uint32_t len = 0;
  1.1133 +  if (NS_SUCCEEDED(transferable->GetAnyTransferData(getter_Copies(bestFlavor), getter_AddRefs(genericDataObj), &len)))
  1.1134 +  {
  1.1135 +    nsAutoTxnsConserveSelection dontSpazMySelection(this);
  1.1136 +    nsAutoString flavor;
  1.1137 +    flavor.AssignWithConversion(bestFlavor);
  1.1138 +    nsAutoString stuffToPaste;
  1.1139 +#ifdef DEBUG_clipboard
  1.1140 +    printf("Got flavor [%s]\n", bestFlavor.get());
  1.1141 +#endif
  1.1142 +
  1.1143 +    bool isSafe = IsSafeToInsertData(aSourceDoc);
  1.1144 +
  1.1145 +    if (0 == nsCRT::strcmp(bestFlavor, kFileMime) ||
  1.1146 +        0 == nsCRT::strcmp(bestFlavor, kJPEGImageMime) ||
  1.1147 +        0 == nsCRT::strcmp(bestFlavor, kJPGImageMime) ||
  1.1148 +        0 == nsCRT::strcmp(bestFlavor, kPNGImageMime) ||
  1.1149 +        0 == nsCRT::strcmp(bestFlavor, kGIFImageMime)) {
  1.1150 +      rv = InsertObject(bestFlavor, genericDataObj, isSafe,
  1.1151 +                        aSourceDoc, aDestinationNode, aDestOffset, aDoDeleteSelection);
  1.1152 +    }
  1.1153 +    else if (0 == nsCRT::strcmp(bestFlavor, kNativeHTMLMime))
  1.1154 +    {
  1.1155 +      // note cf_html uses utf8, hence use length = len, not len/2 as in flavors below
  1.1156 +      nsCOMPtr<nsISupportsCString> textDataObj = do_QueryInterface(genericDataObj);
  1.1157 +      if (textDataObj && len > 0)
  1.1158 +      {
  1.1159 +        nsAutoCString cfhtml;
  1.1160 +        textDataObj->GetData(cfhtml);
  1.1161 +        NS_ASSERTION(cfhtml.Length() <= (len), "Invalid length!");
  1.1162 +        nsXPIDLString cfcontext, cffragment, cfselection; // cfselection left emtpy for now
  1.1163 +
  1.1164 +        rv = ParseCFHTML(cfhtml, getter_Copies(cffragment), getter_Copies(cfcontext));
  1.1165 +        if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty())
  1.1166 +        {
  1.1167 +          nsAutoEditBatch beginBatching(this);
  1.1168 +          rv = DoInsertHTMLWithContext(cffragment,
  1.1169 +                                       cfcontext, cfselection, flavor,
  1.1170 +                                       aSourceDoc,
  1.1171 +                                       aDestinationNode, aDestOffset,
  1.1172 +                                       aDoDeleteSelection,
  1.1173 +                                       isSafe);
  1.1174 +        } else {
  1.1175 +          // In some platforms (like Linux), the clipboard might return data
  1.1176 +          // requested for unknown flavors (for example:
  1.1177 +          // application/x-moz-nativehtml).  In this case, treat the data
  1.1178 +          // to be pasted as mere HTML to get the best chance of pasting it
  1.1179 +          // correctly.
  1.1180 +          bestFlavor.AssignLiteral(kHTMLMime);
  1.1181 +          // Fall through the next case
  1.1182 +        }
  1.1183 +      }
  1.1184 +    }
  1.1185 +    if (0 == nsCRT::strcmp(bestFlavor, kHTMLMime) ||
  1.1186 +        0 == nsCRT::strcmp(bestFlavor, kUnicodeMime) ||
  1.1187 +        0 == nsCRT::strcmp(bestFlavor, kMozTextInternal)) {
  1.1188 +      nsCOMPtr<nsISupportsString> textDataObj = do_QueryInterface(genericDataObj);
  1.1189 +      if (textDataObj && len > 0) {
  1.1190 +        nsAutoString text;
  1.1191 +        textDataObj->GetData(text);
  1.1192 +        NS_ASSERTION(text.Length() <= (len/2), "Invalid length!");
  1.1193 +        stuffToPaste.Assign(text.get(), len / 2);
  1.1194 +      } else {
  1.1195 +        nsCOMPtr<nsISupportsCString> textDataObj(do_QueryInterface(genericDataObj));
  1.1196 +        if (textDataObj && len > 0) {
  1.1197 +          nsAutoCString text;
  1.1198 +          textDataObj->GetData(text);
  1.1199 +          NS_ASSERTION(text.Length() <= len, "Invalid length!");
  1.1200 +          stuffToPaste.Assign(NS_ConvertUTF8toUTF16(Substring(text, 0, len)));
  1.1201 +        }
  1.1202 +      }
  1.1203 +
  1.1204 +      if (!stuffToPaste.IsEmpty()) {
  1.1205 +        nsAutoEditBatch beginBatching(this);
  1.1206 +        if (0 == nsCRT::strcmp(bestFlavor, kHTMLMime)) {
  1.1207 +          rv = DoInsertHTMLWithContext(stuffToPaste,
  1.1208 +                                       aContextStr, aInfoStr, flavor,
  1.1209 +                                       aSourceDoc,
  1.1210 +                                       aDestinationNode, aDestOffset,
  1.1211 +                                       aDoDeleteSelection,
  1.1212 +                                       isSafe);
  1.1213 +        } else {
  1.1214 +          rv = InsertTextAt(stuffToPaste, aDestinationNode, aDestOffset, aDoDeleteSelection);
  1.1215 +        }
  1.1216 +      }
  1.1217 +    }
  1.1218 +  }
  1.1219 +
  1.1220 +  // Try to scroll the selection into view if the paste succeeded
  1.1221 +  if (NS_SUCCEEDED(rv))
  1.1222 +    ScrollSelectionIntoView(false);
  1.1223 +
  1.1224 +  return rv;
  1.1225 +}
  1.1226 +
  1.1227 +static void
  1.1228 +GetStringFromDataTransfer(nsIDOMDataTransfer *aDataTransfer, const nsAString& aType,
  1.1229 +                          int32_t aIndex, nsAString& aOutputString)
  1.1230 +{
  1.1231 +  nsCOMPtr<nsIVariant> variant;
  1.1232 +  aDataTransfer->MozGetDataAt(aType, aIndex, getter_AddRefs(variant));
  1.1233 +  if (variant)
  1.1234 +    variant->GetAsAString(aOutputString);
  1.1235 +}
  1.1236 +
  1.1237 +nsresult nsHTMLEditor::InsertFromDataTransfer(DataTransfer *aDataTransfer,
  1.1238 +                                              int32_t aIndex,
  1.1239 +                                              nsIDOMDocument *aSourceDoc,
  1.1240 +                                              nsIDOMNode *aDestinationNode,
  1.1241 +                                              int32_t aDestOffset,
  1.1242 +                                              bool aDoDeleteSelection)
  1.1243 +{
  1.1244 +  ErrorResult rv;
  1.1245 +  nsRefPtr<DOMStringList> types = aDataTransfer->MozTypesAt(aIndex, rv);
  1.1246 +  if (rv.Failed()) {
  1.1247 +    return rv.ErrorCode();
  1.1248 +  }
  1.1249 +
  1.1250 +  bool hasPrivateHTMLFlavor = types->Contains(NS_LITERAL_STRING(kHTMLContext));
  1.1251 +
  1.1252 +  bool isText = IsPlaintextEditor();
  1.1253 +  bool isSafe = IsSafeToInsertData(aSourceDoc);
  1.1254 +
  1.1255 +  uint32_t length = types->Length();
  1.1256 +  for (uint32_t t = 0; t < length; t++) {
  1.1257 +    nsAutoString type;
  1.1258 +    types->Item(t, type);
  1.1259 +
  1.1260 +    if (!isText) {
  1.1261 +      if (type.EqualsLiteral(kFileMime) ||
  1.1262 +          type.EqualsLiteral(kJPEGImageMime) ||
  1.1263 +          type.EqualsLiteral(kJPGImageMime) ||
  1.1264 +          type.EqualsLiteral(kPNGImageMime) ||
  1.1265 +          type.EqualsLiteral(kGIFImageMime)) {
  1.1266 +        nsCOMPtr<nsIVariant> variant;
  1.1267 +        aDataTransfer->MozGetDataAt(type, aIndex, getter_AddRefs(variant));
  1.1268 +        if (variant) {
  1.1269 +          nsCOMPtr<nsISupports> object;
  1.1270 +          variant->GetAsISupports(getter_AddRefs(object));
  1.1271 +          return InsertObject(NS_ConvertUTF16toUTF8(type).get(), object, isSafe,
  1.1272 +                              aSourceDoc, aDestinationNode, aDestOffset, aDoDeleteSelection);
  1.1273 +        }
  1.1274 +      }
  1.1275 +      else if (!hasPrivateHTMLFlavor && type.EqualsLiteral(kNativeHTMLMime)) {
  1.1276 +        nsAutoString text;
  1.1277 +        GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kNativeHTMLMime), aIndex, text);
  1.1278 +        NS_ConvertUTF16toUTF8 cfhtml(text);
  1.1279 +
  1.1280 +        nsXPIDLString cfcontext, cffragment, cfselection; // cfselection left emtpy for now
  1.1281 +
  1.1282 +        nsresult rv = ParseCFHTML(cfhtml, getter_Copies(cffragment), getter_Copies(cfcontext));
  1.1283 +        if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty())
  1.1284 +        {
  1.1285 +          nsAutoEditBatch beginBatching(this);
  1.1286 +          return DoInsertHTMLWithContext(cffragment,
  1.1287 +                                         cfcontext, cfselection, type,
  1.1288 +                                         aSourceDoc,
  1.1289 +                                         aDestinationNode, aDestOffset,
  1.1290 +                                         aDoDeleteSelection,
  1.1291 +                                         isSafe);
  1.1292 +        }
  1.1293 +      }
  1.1294 +      else if (type.EqualsLiteral(kHTMLMime)) {
  1.1295 +        nsAutoString text, contextString, infoString;
  1.1296 +        GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
  1.1297 +        GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), aIndex, contextString);
  1.1298 +        GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), aIndex, infoString);
  1.1299 +
  1.1300 +        nsAutoEditBatch beginBatching(this);
  1.1301 +        if (type.EqualsLiteral(kHTMLMime)) {
  1.1302 +          return DoInsertHTMLWithContext(text,
  1.1303 +                                         contextString, infoString, type,
  1.1304 +                                         aSourceDoc,
  1.1305 +                                         aDestinationNode, aDestOffset,
  1.1306 +                                         aDoDeleteSelection,
  1.1307 +                                         isSafe);
  1.1308 +        }
  1.1309 +      }
  1.1310 +    }
  1.1311 +
  1.1312 +    if (type.EqualsLiteral(kTextMime) ||
  1.1313 +        type.EqualsLiteral(kMozTextInternal)) {
  1.1314 +      nsAutoString text;
  1.1315 +      GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
  1.1316 +
  1.1317 +      nsAutoEditBatch beginBatching(this);
  1.1318 +      return InsertTextAt(text, aDestinationNode, aDestOffset, aDoDeleteSelection);
  1.1319 +    }
  1.1320 +  }
  1.1321 +
  1.1322 +  return NS_OK;
  1.1323 +}
  1.1324 +
  1.1325 +bool nsHTMLEditor::HavePrivateHTMLFlavor(nsIClipboard *aClipboard)
  1.1326 +{
  1.1327 +  // check the clipboard for our special kHTMLContext flavor.  If that is there, we know
  1.1328 +  // we have our own internal html format on clipboard.
  1.1329 +
  1.1330 +  NS_ENSURE_TRUE(aClipboard, false);
  1.1331 +  bool bHavePrivateHTMLFlavor = false;
  1.1332 +
  1.1333 +  const char* flavArray[] = { kHTMLContext };
  1.1334 +
  1.1335 +  if (NS_SUCCEEDED(aClipboard->HasDataMatchingFlavors(flavArray,
  1.1336 +    ArrayLength(flavArray), nsIClipboard::kGlobalClipboard,
  1.1337 +    &bHavePrivateHTMLFlavor)))
  1.1338 +    return bHavePrivateHTMLFlavor;
  1.1339 +
  1.1340 +  return false;
  1.1341 +}
  1.1342 +
  1.1343 +
  1.1344 +NS_IMETHODIMP nsHTMLEditor::Paste(int32_t aSelectionType)
  1.1345 +{
  1.1346 +  if (!FireClipboardEvent(NS_PASTE, aSelectionType))
  1.1347 +    return NS_OK;
  1.1348 +
  1.1349 +  // Get Clipboard Service
  1.1350 +  nsresult rv;
  1.1351 +  nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
  1.1352 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1353 +
  1.1354 +  // find out if we have our internal html flavor on the clipboard.  We don't want to mess
  1.1355 +  // around with cfhtml if we do.
  1.1356 +  bool bHavePrivateHTMLFlavor = HavePrivateHTMLFlavor(clipboard);
  1.1357 +
  1.1358 +  // Get the nsITransferable interface for getting the data from the clipboard
  1.1359 +  nsCOMPtr<nsITransferable> trans;
  1.1360 +  rv = PrepareHTMLTransferable(getter_AddRefs(trans), bHavePrivateHTMLFlavor);
  1.1361 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1362 +  NS_ENSURE_TRUE(trans, NS_ERROR_FAILURE);
  1.1363 +  // Get the Data from the clipboard
  1.1364 +  rv = clipboard->GetData(trans, aSelectionType);
  1.1365 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1366 +  if (!IsModifiable()) {
  1.1367 +    return NS_OK;
  1.1368 +  }
  1.1369 +
  1.1370 +  // also get additional html copy hints, if present
  1.1371 +  nsAutoString contextStr, infoStr;
  1.1372 +
  1.1373 +  // also get additional html copy hints, if present
  1.1374 +  if (bHavePrivateHTMLFlavor)
  1.1375 +  {
  1.1376 +    nsCOMPtr<nsISupports> contextDataObj, infoDataObj;
  1.1377 +    uint32_t contextLen, infoLen;
  1.1378 +    nsCOMPtr<nsISupportsString> textDataObj;
  1.1379 +
  1.1380 +    nsCOMPtr<nsITransferable> contextTrans =
  1.1381 +                  do_CreateInstance("@mozilla.org/widget/transferable;1");
  1.1382 +    NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER);
  1.1383 +    contextTrans->Init(nullptr);
  1.1384 +    contextTrans->AddDataFlavor(kHTMLContext);
  1.1385 +    clipboard->GetData(contextTrans, aSelectionType);
  1.1386 +    contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen);
  1.1387 +
  1.1388 +    nsCOMPtr<nsITransferable> infoTrans =
  1.1389 +                  do_CreateInstance("@mozilla.org/widget/transferable;1");
  1.1390 +    NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER);
  1.1391 +    infoTrans->Init(nullptr);
  1.1392 +    infoTrans->AddDataFlavor(kHTMLInfo);
  1.1393 +    clipboard->GetData(infoTrans, aSelectionType);
  1.1394 +    infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen);
  1.1395 +
  1.1396 +    if (contextDataObj)
  1.1397 +    {
  1.1398 +      nsAutoString text;
  1.1399 +      textDataObj = do_QueryInterface(contextDataObj);
  1.1400 +      textDataObj->GetData(text);
  1.1401 +      NS_ASSERTION(text.Length() <= (contextLen/2), "Invalid length!");
  1.1402 +      contextStr.Assign(text.get(), contextLen / 2);
  1.1403 +    }
  1.1404 +
  1.1405 +    if (infoDataObj)
  1.1406 +    {
  1.1407 +      nsAutoString text;
  1.1408 +      textDataObj = do_QueryInterface(infoDataObj);
  1.1409 +      textDataObj->GetData(text);
  1.1410 +      NS_ASSERTION(text.Length() <= (infoLen/2), "Invalid length!");
  1.1411 +      infoStr.Assign(text.get(), infoLen / 2);
  1.1412 +    }
  1.1413 +  }
  1.1414 +
  1.1415 +  // handle transferable hooks
  1.1416 +  nsCOMPtr<nsIDOMDocument> domdoc;
  1.1417 +  GetDocument(getter_AddRefs(domdoc));
  1.1418 +  if (!nsEditorHookUtils::DoInsertionHook(domdoc, nullptr, trans))
  1.1419 +    return NS_OK;
  1.1420 +
  1.1421 +  return InsertFromTransferable(trans, nullptr, contextStr, infoStr,
  1.1422 +                                nullptr, 0, true);
  1.1423 +}
  1.1424 +
  1.1425 +NS_IMETHODIMP nsHTMLEditor::PasteTransferable(nsITransferable *aTransferable)
  1.1426 +{
  1.1427 +  // Use an invalid value for the clipboard type as data comes from aTransferable
  1.1428 +  // and we don't currently implement a way to put that in the data transfer yet.
  1.1429 +  if (!FireClipboardEvent(NS_PASTE, nsIClipboard::kGlobalClipboard))
  1.1430 +    return NS_OK;
  1.1431 +
  1.1432 +  // handle transferable hooks
  1.1433 +  nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
  1.1434 +  if (!nsEditorHookUtils::DoInsertionHook(domdoc, nullptr, aTransferable))
  1.1435 +    return NS_OK;
  1.1436 +
  1.1437 +  nsAutoString contextStr, infoStr;
  1.1438 +  return InsertFromTransferable(aTransferable, nullptr, contextStr, infoStr,
  1.1439 +                                nullptr, 0, true);
  1.1440 +}
  1.1441 +
  1.1442 +// 
  1.1443 +// HTML PasteNoFormatting. Ignore any HTML styles and formating in paste source
  1.1444 +//
  1.1445 +NS_IMETHODIMP nsHTMLEditor::PasteNoFormatting(int32_t aSelectionType)
  1.1446 +{
  1.1447 +  if (!FireClipboardEvent(NS_PASTE, aSelectionType))
  1.1448 +    return NS_OK;
  1.1449 +
  1.1450 +  ForceCompositionEnd();
  1.1451 +
  1.1452 +  // Get Clipboard Service
  1.1453 +  nsresult rv;
  1.1454 +  nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
  1.1455 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1456 +
  1.1457 +  // Get the nsITransferable interface for getting the data from the clipboard.
  1.1458 +  // use nsPlaintextEditor::PrepareTransferable() to force unicode plaintext data.
  1.1459 +  nsCOMPtr<nsITransferable> trans;
  1.1460 +  rv = nsPlaintextEditor::PrepareTransferable(getter_AddRefs(trans));
  1.1461 +  if (NS_SUCCEEDED(rv) && trans)
  1.1462 +  {
  1.1463 +    // Get the Data from the clipboard  
  1.1464 +    if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable())
  1.1465 +    {
  1.1466 +      const nsAFlatString& empty = EmptyString();
  1.1467 +      rv = InsertFromTransferable(trans, nullptr, empty, empty, nullptr, 0,
  1.1468 +                                  true);
  1.1469 +    }
  1.1470 +  }
  1.1471 +
  1.1472 +  return rv;
  1.1473 +}
  1.1474 +
  1.1475 +
  1.1476 +// The following arrays contain the MIME types that we can paste. The arrays
  1.1477 +// are used by CanPaste() and CanPasteTransferable() below.
  1.1478 +
  1.1479 +static const char* textEditorFlavors[] = { kUnicodeMime };
  1.1480 +static const char* textHtmlEditorFlavors[] = { kUnicodeMime, kHTMLMime,
  1.1481 +                                               kJPEGImageMime, kJPGImageMime,
  1.1482 +                                               kPNGImageMime, kGIFImageMime };
  1.1483 +
  1.1484 +NS_IMETHODIMP nsHTMLEditor::CanPaste(int32_t aSelectionType, bool *aCanPaste)
  1.1485 +{
  1.1486 +  NS_ENSURE_ARG_POINTER(aCanPaste);
  1.1487 +  *aCanPaste = false;
  1.1488 +
  1.1489 +  // can't paste if readonly
  1.1490 +  if (!IsModifiable()) {
  1.1491 +    return NS_OK;
  1.1492 +  }
  1.1493 +
  1.1494 +  nsresult rv;
  1.1495 +  nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
  1.1496 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1497 +
  1.1498 +  bool haveFlavors;
  1.1499 +
  1.1500 +  // Use the flavors depending on the current editor mask
  1.1501 +  if (IsPlaintextEditor())
  1.1502 +    rv = clipboard->HasDataMatchingFlavors(textEditorFlavors,
  1.1503 +                                           ArrayLength(textEditorFlavors),
  1.1504 +                                           aSelectionType, &haveFlavors);
  1.1505 +  else
  1.1506 +    rv = clipboard->HasDataMatchingFlavors(textHtmlEditorFlavors,
  1.1507 +                                           ArrayLength(textHtmlEditorFlavors),
  1.1508 +                                           aSelectionType, &haveFlavors);
  1.1509 +
  1.1510 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1511 +
  1.1512 +  *aCanPaste = haveFlavors;
  1.1513 +  return NS_OK;
  1.1514 +}
  1.1515 +
  1.1516 +NS_IMETHODIMP nsHTMLEditor::CanPasteTransferable(nsITransferable *aTransferable, bool *aCanPaste)
  1.1517 +{
  1.1518 +  NS_ENSURE_ARG_POINTER(aCanPaste);
  1.1519 +
  1.1520 +  // can't paste if readonly
  1.1521 +  if (!IsModifiable()) {
  1.1522 +    *aCanPaste = false;
  1.1523 +    return NS_OK;
  1.1524 +  }
  1.1525 +
  1.1526 +  // If |aTransferable| is null, assume that a paste will succeed.
  1.1527 +  if (!aTransferable) {
  1.1528 +    *aCanPaste = true;
  1.1529 +    return NS_OK;
  1.1530 +  }
  1.1531 +
  1.1532 +  // Peek in |aTransferable| to see if it contains a supported MIME type.
  1.1533 +
  1.1534 +  // Use the flavors depending on the current editor mask
  1.1535 +  const char ** flavors;
  1.1536 +  unsigned length;
  1.1537 +  if (IsPlaintextEditor()) {
  1.1538 +    flavors = textEditorFlavors;
  1.1539 +    length = ArrayLength(textEditorFlavors);
  1.1540 +  } else {
  1.1541 +    flavors = textHtmlEditorFlavors;
  1.1542 +    length = ArrayLength(textHtmlEditorFlavors);
  1.1543 +  }
  1.1544 +
  1.1545 +  for (unsigned int i = 0; i < length; i++, flavors++) {
  1.1546 +    nsCOMPtr<nsISupports> data;
  1.1547 +    uint32_t dataLen;
  1.1548 +    nsresult rv = aTransferable->GetTransferData(*flavors,
  1.1549 +                                                 getter_AddRefs(data),
  1.1550 +                                                 &dataLen);
  1.1551 +    if (NS_SUCCEEDED(rv) && data) {
  1.1552 +      *aCanPaste = true;
  1.1553 +      return NS_OK;
  1.1554 +    }
  1.1555 +  }
  1.1556 +
  1.1557 +  *aCanPaste = false;
  1.1558 +  return NS_OK;
  1.1559 +}
  1.1560 +
  1.1561 +
  1.1562 +//
  1.1563 +// HTML PasteAsQuotation: Paste in a blockquote type=cite
  1.1564 +//
  1.1565 +NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation(int32_t aSelectionType)
  1.1566 +{
  1.1567 +  if (IsPlaintextEditor())
  1.1568 +    return PasteAsPlaintextQuotation(aSelectionType);
  1.1569 +
  1.1570 +  nsAutoString citation;
  1.1571 +  return PasteAsCitedQuotation(citation, aSelectionType);
  1.1572 +}
  1.1573 +
  1.1574 +NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsAString & aCitation,
  1.1575 +                                                  int32_t aSelectionType)
  1.1576 +{
  1.1577 +  nsAutoEditBatch beginBatching(this);
  1.1578 +  nsAutoRules beginRulesSniffing(this, EditAction::insertQuotation, nsIEditor::eNext);
  1.1579 +
  1.1580 +  // get selection
  1.1581 +  nsRefPtr<Selection> selection = GetSelection();
  1.1582 +  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1.1583 +
  1.1584 +  // give rules a chance to handle or cancel
  1.1585 +  nsTextRulesInfo ruleInfo(EditAction::insertElement);
  1.1586 +  bool cancel, handled;
  1.1587 +  // Protect the edit rules object from dying
  1.1588 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1.1589 +  nsresult rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
  1.1590 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1591 +  if (cancel || handled) {
  1.1592 +    return NS_OK; // rules canceled the operation
  1.1593 +  }
  1.1594 +
  1.1595 +  nsCOMPtr<nsIDOMNode> newNode;
  1.1596 +  rv = DeleteSelectionAndCreateNode(NS_LITERAL_STRING("blockquote"), getter_AddRefs(newNode));
  1.1597 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1598 +  NS_ENSURE_TRUE(newNode, NS_ERROR_NULL_POINTER);
  1.1599 +
  1.1600 +  // Try to set type=cite.  Ignore it if this fails.
  1.1601 +  nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(newNode);
  1.1602 +  if (newElement) {
  1.1603 +    newElement->SetAttribute(NS_LITERAL_STRING("type"), NS_LITERAL_STRING("cite"));
  1.1604 +  }
  1.1605 +
  1.1606 +  // Set the selection to the underneath the node we just inserted:
  1.1607 +  rv = selection->Collapse(newNode, 0);
  1.1608 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1609 +
  1.1610 +  return Paste(aSelectionType);
  1.1611 +}
  1.1612 +
  1.1613 +//
  1.1614 +// Paste a plaintext quotation
  1.1615 +//
  1.1616 +NS_IMETHODIMP nsHTMLEditor::PasteAsPlaintextQuotation(int32_t aSelectionType)
  1.1617 +{
  1.1618 +  // Get Clipboard Service
  1.1619 +  nsresult rv;
  1.1620 +  nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
  1.1621 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1622 +
  1.1623 +  // Create generic Transferable for getting the data
  1.1624 +  nsCOMPtr<nsITransferable> trans =
  1.1625 +                 do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
  1.1626 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1627 +  NS_ENSURE_TRUE(trans, NS_ERROR_FAILURE);
  1.1628 +
  1.1629 +  nsCOMPtr<nsIDocument> destdoc = GetDocument();
  1.1630 +  nsILoadContext* loadContext = destdoc ? destdoc->GetLoadContext() : nullptr;
  1.1631 +  trans->Init(loadContext);
  1.1632 +
  1.1633 +  // We only handle plaintext pastes here
  1.1634 +  trans->AddDataFlavor(kUnicodeMime);
  1.1635 +
  1.1636 +  // Get the Data from the clipboard
  1.1637 +  clipboard->GetData(trans, aSelectionType);
  1.1638 +
  1.1639 +  // Now we ask the transferable for the data
  1.1640 +  // it still owns the data, we just have a pointer to it.
  1.1641 +  // If it can't support a "text" output of the data the call will fail
  1.1642 +  nsCOMPtr<nsISupports> genericDataObj;
  1.1643 +  uint32_t len = 0;
  1.1644 +  char* flav = 0;
  1.1645 +  rv = trans->GetAnyTransferData(&flav, getter_AddRefs(genericDataObj), &len);
  1.1646 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1647 +
  1.1648 +  if (flav && 0 == nsCRT::strcmp(flav, kUnicodeMime))
  1.1649 +  {
  1.1650 +#ifdef DEBUG_clipboard
  1.1651 +    printf("Got flavor [%s]\n", flav);
  1.1652 +#endif
  1.1653 +    nsCOMPtr<nsISupportsString> textDataObj = do_QueryInterface(genericDataObj);
  1.1654 +    if (textDataObj && len > 0)
  1.1655 +    {
  1.1656 +      nsAutoString stuffToPaste;
  1.1657 +      textDataObj->GetData(stuffToPaste);
  1.1658 +      NS_ASSERTION(stuffToPaste.Length() <= (len/2), "Invalid length!");
  1.1659 +      nsAutoEditBatch beginBatching(this);
  1.1660 +      rv = InsertAsPlaintextQuotation(stuffToPaste, true, 0);
  1.1661 +    }
  1.1662 +  }
  1.1663 +  NS_Free(flav);
  1.1664 +
  1.1665 +  return rv;
  1.1666 +}
  1.1667 +
  1.1668 +NS_IMETHODIMP
  1.1669 +nsHTMLEditor::InsertTextWithQuotations(const nsAString &aStringToInsert)
  1.1670 +{
  1.1671 +  if (mWrapToWindow)
  1.1672 +    return InsertText(aStringToInsert);
  1.1673 +
  1.1674 +  // The whole operation should be undoable in one transaction:
  1.1675 +  BeginTransaction();
  1.1676 +
  1.1677 +  // We're going to loop over the string, collecting up a "hunk"
  1.1678 +  // that's all the same type (quoted or not),
  1.1679 +  // Whenever the quotedness changes (or we reach the string's end)
  1.1680 +  // we will insert the hunk all at once, quoted or non.
  1.1681 +
  1.1682 +  static const char16_t cite('>');
  1.1683 +  bool curHunkIsQuoted = (aStringToInsert.First() == cite);
  1.1684 +
  1.1685 +  nsAString::const_iterator hunkStart, strEnd;
  1.1686 +  aStringToInsert.BeginReading(hunkStart);
  1.1687 +  aStringToInsert.EndReading(strEnd);
  1.1688 +
  1.1689 +  // In the loop below, we only look for DOM newlines (\n),
  1.1690 +  // because we don't have a FindChars method that can look
  1.1691 +  // for both \r and \n.  \r is illegal in the dom anyway,
  1.1692 +  // but in debug builds, let's take the time to verify that
  1.1693 +  // there aren't any there:
  1.1694 +#ifdef DEBUG
  1.1695 +  nsAString::const_iterator dbgStart (hunkStart);
  1.1696 +  if (FindCharInReadable('\r', dbgStart, strEnd))
  1.1697 +    NS_ASSERTION(false,
  1.1698 +            "Return characters in DOM! InsertTextWithQuotations may be wrong");
  1.1699 +#endif /* DEBUG */
  1.1700 +
  1.1701 +  // Loop over lines:
  1.1702 +  nsresult rv = NS_OK;
  1.1703 +  nsAString::const_iterator lineStart (hunkStart);
  1.1704 +  while (1)   // we will break from inside when we run out of newlines
  1.1705 +  {
  1.1706 +    // Search for the end of this line (dom newlines, see above):
  1.1707 +    bool found = FindCharInReadable('\n', lineStart, strEnd);
  1.1708 +    bool quoted = false;
  1.1709 +    if (found)
  1.1710 +    {
  1.1711 +      // if there's another newline, lineStart now points there.
  1.1712 +      // Loop over any consecutive newline chars:
  1.1713 +      nsAString::const_iterator firstNewline (lineStart);
  1.1714 +      while (*lineStart == '\n')
  1.1715 +        ++lineStart;
  1.1716 +      quoted = (*lineStart == cite);
  1.1717 +      if (quoted == curHunkIsQuoted)
  1.1718 +        continue;
  1.1719 +      // else we're changing state, so we need to insert
  1.1720 +      // from curHunk to lineStart then loop around.
  1.1721 +
  1.1722 +      // But if the current hunk is quoted, then we want to make sure
  1.1723 +      // that any extra newlines on the end do not get included in
  1.1724 +      // the quoted section: blank lines flaking a quoted section
  1.1725 +      // should be considered unquoted, so that if the user clicks
  1.1726 +      // there and starts typing, the new text will be outside of
  1.1727 +      // the quoted block.
  1.1728 +      if (curHunkIsQuoted)
  1.1729 +        lineStart = firstNewline;
  1.1730 +    }
  1.1731 +
  1.1732 +    // If no newline found, lineStart is now strEnd and we can finish up,
  1.1733 +    // inserting from curHunk to lineStart then returning.
  1.1734 +    const nsAString &curHunk = Substring(hunkStart, lineStart);
  1.1735 +    nsCOMPtr<nsIDOMNode> dummyNode;
  1.1736 +#ifdef DEBUG_akkana_verbose
  1.1737 +    printf("==== Inserting text as %squoted: ---\n%s---\n",
  1.1738 +           curHunkIsQuoted ? "" : "non-",
  1.1739 +           NS_LossyConvertUTF16toASCII(curHunk).get());
  1.1740 +#endif
  1.1741 +    if (curHunkIsQuoted)
  1.1742 +      rv = InsertAsPlaintextQuotation(curHunk, false,
  1.1743 +                                      getter_AddRefs(dummyNode));
  1.1744 +    else
  1.1745 +      rv = InsertText(curHunk);
  1.1746 +
  1.1747 +    if (!found)
  1.1748 +      break;
  1.1749 +
  1.1750 +    curHunkIsQuoted = quoted;
  1.1751 +    hunkStart = lineStart;
  1.1752 +  }
  1.1753 +
  1.1754 +  EndTransaction();
  1.1755 +
  1.1756 +  return rv;
  1.1757 +}
  1.1758 +
  1.1759 +NS_IMETHODIMP nsHTMLEditor::InsertAsQuotation(const nsAString & aQuotedText,
  1.1760 +                                              nsIDOMNode **aNodeInserted)
  1.1761 +{
  1.1762 +  if (IsPlaintextEditor())
  1.1763 +    return InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted);
  1.1764 +
  1.1765 +  nsAutoString citation;
  1.1766 +  return InsertAsCitedQuotation(aQuotedText, citation, false,
  1.1767 +                                aNodeInserted);
  1.1768 +}
  1.1769 +
  1.1770 +// Insert plaintext as a quotation, with cite marks (e.g. "> ").
  1.1771 +// This differs from its corresponding method in nsPlaintextEditor
  1.1772 +// in that here, quoted material is enclosed in a <pre> tag
  1.1773 +// in order to preserve the original line wrapping.
  1.1774 +NS_IMETHODIMP
  1.1775 +nsHTMLEditor::InsertAsPlaintextQuotation(const nsAString & aQuotedText,
  1.1776 +                                         bool aAddCites,
  1.1777 +                                         nsIDOMNode **aNodeInserted)
  1.1778 +{
  1.1779 +  if (mWrapToWindow)
  1.1780 +    return nsPlaintextEditor::InsertAsQuotation(aQuotedText, aNodeInserted);
  1.1781 +
  1.1782 +  nsCOMPtr<nsIDOMNode> newNode;
  1.1783 +  // get selection
  1.1784 +  nsRefPtr<Selection> selection = GetSelection();
  1.1785 +  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1.1786 +
  1.1787 +  nsAutoEditBatch beginBatching(this);
  1.1788 +  nsAutoRules beginRulesSniffing(this, EditAction::insertQuotation, nsIEditor::eNext);
  1.1789 +
  1.1790 +  // give rules a chance to handle or cancel
  1.1791 +  nsTextRulesInfo ruleInfo(EditAction::insertElement);
  1.1792 +  bool cancel, handled;
  1.1793 +  // Protect the edit rules object from dying
  1.1794 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1.1795 +  nsresult rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
  1.1796 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1797 +  if (cancel || handled) {
  1.1798 +    return NS_OK; // rules canceled the operation
  1.1799 +  }
  1.1800 +
  1.1801 +  // Wrap the inserted quote in a <span> so it won't be wrapped:
  1.1802 +  rv = DeleteSelectionAndCreateNode(NS_LITERAL_STRING("span"), getter_AddRefs(newNode));
  1.1803 +
  1.1804 +  // If this succeeded, then set selection inside the pre
  1.1805 +  // so the inserted text will end up there.
  1.1806 +  // If it failed, we don't care what the return value was,
  1.1807 +  // but we'll fall through and try to insert the text anyway.
  1.1808 +  if (NS_SUCCEEDED(rv) && newNode)
  1.1809 +  {
  1.1810 +    // Add an attribute on the pre node so we'll know it's a quotation.
  1.1811 +    // Do this after the insertion, so that
  1.1812 +    nsCOMPtr<nsIDOMElement> preElement = do_QueryInterface(newNode);
  1.1813 +    if (preElement)
  1.1814 +    {
  1.1815 +      preElement->SetAttribute(NS_LITERAL_STRING("_moz_quote"),
  1.1816 +                               NS_LITERAL_STRING("true"));
  1.1817 +      // turn off wrapping on spans
  1.1818 +      preElement->SetAttribute(NS_LITERAL_STRING("style"),
  1.1819 +                               NS_LITERAL_STRING("white-space: pre;"));
  1.1820 +    }
  1.1821 +    // and set the selection inside it:
  1.1822 +    selection->Collapse(newNode, 0);
  1.1823 +  }
  1.1824 +
  1.1825 +  if (aAddCites)
  1.1826 +    rv = nsPlaintextEditor::InsertAsQuotation(aQuotedText, aNodeInserted);
  1.1827 +  else
  1.1828 +    rv = nsPlaintextEditor::InsertText(aQuotedText);
  1.1829 +  // Note that if !aAddCites, aNodeInserted isn't set.
  1.1830 +  // That's okay because the routines that use aAddCites
  1.1831 +  // don't need to know the inserted node.
  1.1832 +
  1.1833 +  if (aNodeInserted && NS_SUCCEEDED(rv))
  1.1834 +  {
  1.1835 +    *aNodeInserted = newNode;
  1.1836 +    NS_IF_ADDREF(*aNodeInserted);
  1.1837 +  }
  1.1838 +
  1.1839 +  // Set the selection to just after the inserted node:
  1.1840 +  if (NS_SUCCEEDED(rv) && newNode)
  1.1841 +  {
  1.1842 +    int32_t offset;
  1.1843 +    nsCOMPtr<nsIDOMNode> parent = GetNodeLocation(newNode, &offset);
  1.1844 +    if (parent) {
  1.1845 +      selection->Collapse(parent, offset + 1);
  1.1846 +    }
  1.1847 +  }
  1.1848 +  return rv;
  1.1849 +}
  1.1850 +
  1.1851 +NS_IMETHODIMP
  1.1852 +nsHTMLEditor::StripCites()
  1.1853 +{
  1.1854 +  return nsPlaintextEditor::StripCites();
  1.1855 +}
  1.1856 +
  1.1857 +NS_IMETHODIMP
  1.1858 +nsHTMLEditor::Rewrap(bool aRespectNewlines)
  1.1859 +{
  1.1860 +  return nsPlaintextEditor::Rewrap(aRespectNewlines);
  1.1861 +}
  1.1862 +
  1.1863 +NS_IMETHODIMP
  1.1864 +nsHTMLEditor::InsertAsCitedQuotation(const nsAString & aQuotedText,
  1.1865 +                                     const nsAString & aCitation,
  1.1866 +                                     bool aInsertHTML,
  1.1867 +                                     nsIDOMNode **aNodeInserted)
  1.1868 +{
  1.1869 +  // Don't let anyone insert html into a "plaintext" editor:
  1.1870 +  if (IsPlaintextEditor())
  1.1871 +  {
  1.1872 +    NS_ASSERTION(!aInsertHTML, "InsertAsCitedQuotation: trying to insert html into plaintext editor");
  1.1873 +    return InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted);
  1.1874 +  }
  1.1875 +
  1.1876 +  nsCOMPtr<nsIDOMNode> newNode;
  1.1877 +
  1.1878 +  // get selection
  1.1879 +  nsRefPtr<Selection> selection = GetSelection();
  1.1880 +  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1.1881 +
  1.1882 +  nsAutoEditBatch beginBatching(this);
  1.1883 +  nsAutoRules beginRulesSniffing(this, EditAction::insertQuotation, nsIEditor::eNext);
  1.1884 +
  1.1885 +  // give rules a chance to handle or cancel
  1.1886 +  nsTextRulesInfo ruleInfo(EditAction::insertElement);
  1.1887 +  bool cancel, handled;
  1.1888 +  // Protect the edit rules object from dying
  1.1889 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1.1890 +  nsresult rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
  1.1891 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1892 +  if (cancel || handled) {
  1.1893 +    return NS_OK; // rules canceled the operation
  1.1894 +  }
  1.1895 +
  1.1896 +  rv = DeleteSelectionAndCreateNode(NS_LITERAL_STRING("blockquote"), getter_AddRefs(newNode));
  1.1897 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1898 +  NS_ENSURE_TRUE(newNode, NS_ERROR_NULL_POINTER);
  1.1899 +
  1.1900 +  // Try to set type=cite.  Ignore it if this fails.
  1.1901 +  nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(newNode);
  1.1902 +  if (newElement)
  1.1903 +  {
  1.1904 +    NS_NAMED_LITERAL_STRING(citeStr, "cite");
  1.1905 +    newElement->SetAttribute(NS_LITERAL_STRING("type"), citeStr);
  1.1906 +
  1.1907 +    if (!aCitation.IsEmpty())
  1.1908 +      newElement->SetAttribute(citeStr, aCitation);
  1.1909 +
  1.1910 +    // Set the selection inside the blockquote so aQuotedText will go there:
  1.1911 +    selection->Collapse(newNode, 0);
  1.1912 +  }
  1.1913 +
  1.1914 +  if (aInsertHTML)
  1.1915 +    rv = LoadHTML(aQuotedText);
  1.1916 +  else
  1.1917 +    rv = InsertText(aQuotedText);  // XXX ignore charset
  1.1918 +
  1.1919 +  if (aNodeInserted && NS_SUCCEEDED(rv))
  1.1920 +  {
  1.1921 +    *aNodeInserted = newNode;
  1.1922 +    NS_IF_ADDREF(*aNodeInserted);
  1.1923 +  }
  1.1924 +
  1.1925 +  // Set the selection to just after the inserted node:
  1.1926 +  if (NS_SUCCEEDED(rv) && newNode)
  1.1927 +  {
  1.1928 +    int32_t offset;
  1.1929 +    nsCOMPtr<nsIDOMNode> parent = GetNodeLocation(newNode, &offset);
  1.1930 +    if (parent) {
  1.1931 +      selection->Collapse(parent, offset + 1);
  1.1932 +    }
  1.1933 +  }
  1.1934 +  return rv;
  1.1935 +}
  1.1936 +
  1.1937 +
  1.1938 +void RemoveBodyAndHead(nsIDOMNode *aNode)
  1.1939 +{
  1.1940 +  if (!aNode)
  1.1941 +    return;
  1.1942 +
  1.1943 +  nsCOMPtr<nsIDOMNode> tmp, child, body, head;
  1.1944 +  // find the body and head nodes if any.
  1.1945 +  // look only at immediate children of aNode.
  1.1946 +  aNode->GetFirstChild(getter_AddRefs(child));
  1.1947 +  while (child)
  1.1948 +  {
  1.1949 +    if (nsTextEditUtils::IsBody(child))
  1.1950 +    {
  1.1951 +      body = child;
  1.1952 +    }
  1.1953 +    else if (nsEditor::NodeIsType(child, nsEditProperty::head))
  1.1954 +    {
  1.1955 +      head = child;
  1.1956 +    }
  1.1957 +    child->GetNextSibling(getter_AddRefs(tmp));
  1.1958 +    child = tmp;
  1.1959 +  }
  1.1960 +  if (head)
  1.1961 +  {
  1.1962 +    aNode->RemoveChild(head, getter_AddRefs(tmp));
  1.1963 +  }
  1.1964 +  if (body)
  1.1965 +  {
  1.1966 +    body->GetFirstChild(getter_AddRefs(child));
  1.1967 +    while (child)
  1.1968 +    {
  1.1969 +      aNode->InsertBefore(child, body, getter_AddRefs(tmp));
  1.1970 +      body->GetFirstChild(getter_AddRefs(child));
  1.1971 +    }
  1.1972 +    aNode->RemoveChild(body, getter_AddRefs(tmp));
  1.1973 +  }
  1.1974 +}
  1.1975 +
  1.1976 +/**
  1.1977 + * This function finds the target node that we will be pasting into. aStart is
  1.1978 + * the context that we're given and aResult will be the target. Initially,
  1.1979 + * *aResult must be nullptr.
  1.1980 + *
  1.1981 + * The target for a paste is found by either finding the node that contains
  1.1982 + * the magical comment node containing kInsertCookie or, failing that, the
  1.1983 + * firstChild of the firstChild (until we reach a leaf).
  1.1984 + */
  1.1985 +nsresult FindTargetNode(nsIDOMNode *aStart, nsCOMPtr<nsIDOMNode> &aResult)
  1.1986 +{
  1.1987 +  NS_ENSURE_TRUE(aStart, NS_OK);
  1.1988 +
  1.1989 +  nsCOMPtr<nsIDOMNode> child, tmp;
  1.1990 +
  1.1991 +  nsresult rv = aStart->GetFirstChild(getter_AddRefs(child));
  1.1992 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1993 +
  1.1994 +  if (!child)
  1.1995 +  {
  1.1996 +    // If the current result is nullptr, then aStart is a leaf, and is the
  1.1997 +    // fallback result.
  1.1998 +    if (!aResult)
  1.1999 +      aResult = aStart;
  1.2000 +
  1.2001 +    return NS_OK;
  1.2002 +  }
  1.2003 +
  1.2004 +  do
  1.2005 +  {
  1.2006 +    // Is this child the magical cookie?
  1.2007 +    nsCOMPtr<nsIDOMComment> comment = do_QueryInterface(child);
  1.2008 +    if (comment)
  1.2009 +    {
  1.2010 +      nsAutoString data;
  1.2011 +      rv = comment->GetData(data);
  1.2012 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2013 +
  1.2014 +      if (data.EqualsLiteral(kInsertCookie))
  1.2015 +      {
  1.2016 +        // Yes it is! Return an error so we bubble out and short-circuit the
  1.2017 +        // search.
  1.2018 +        aResult = aStart;
  1.2019 +
  1.2020 +        // Note: it doesn't matter if this fails.
  1.2021 +        aStart->RemoveChild(child, getter_AddRefs(tmp));
  1.2022 +
  1.2023 +        return NS_FOUND_TARGET;
  1.2024 +      }
  1.2025 +    }
  1.2026 +
  1.2027 +    // Note: Don't use NS_ENSURE_* here since we return a failure result to
  1.2028 +    // inicate that we found the magical cookie and we don't want to spam the
  1.2029 +    // console.
  1.2030 +    rv = FindTargetNode(child, aResult);
  1.2031 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2032 +
  1.2033 +    rv = child->GetNextSibling(getter_AddRefs(tmp));
  1.2034 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2035 +
  1.2036 +    child = tmp;
  1.2037 +  } while (child);
  1.2038 +
  1.2039 +  return NS_OK;
  1.2040 +}
  1.2041 +
  1.2042 +nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(const nsAString &aInputString,
  1.2043 +                                                  const nsAString & aContextStr,
  1.2044 +                                                  const nsAString & aInfoStr,
  1.2045 +                                                  nsCOMPtr<nsIDOMNode> *outFragNode,
  1.2046 +                                                  nsCOMPtr<nsIDOMNode> *outStartNode,
  1.2047 +                                                  nsCOMPtr<nsIDOMNode> *outEndNode,
  1.2048 +                                                  int32_t *outStartOffset,
  1.2049 +                                                  int32_t *outEndOffset,
  1.2050 +                                                  bool aTrustedInput)
  1.2051 +{
  1.2052 +  NS_ENSURE_TRUE(outFragNode && outStartNode && outEndNode, NS_ERROR_NULL_POINTER);
  1.2053 +  nsCOMPtr<nsIDOMDocumentFragment> docfrag;
  1.2054 +  nsCOMPtr<nsIDOMNode> contextAsNode, tmp;
  1.2055 +  nsresult rv = NS_OK;
  1.2056 +
  1.2057 +  nsCOMPtr<nsIDocument> doc = GetDocument();
  1.2058 +  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
  1.2059 +
  1.2060 +  // if we have context info, create a fragment for that
  1.2061 +  nsCOMPtr<nsIDOMDocumentFragment> contextfrag;
  1.2062 +  nsCOMPtr<nsIDOMNode> contextLeaf, junk;
  1.2063 +  if (!aContextStr.IsEmpty())
  1.2064 +  {
  1.2065 +    rv = ParseFragment(aContextStr, nullptr, doc, address_of(contextAsNode),
  1.2066 +                       aTrustedInput);
  1.2067 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2068 +    NS_ENSURE_TRUE(contextAsNode, NS_ERROR_FAILURE);
  1.2069 +
  1.2070 +    rv = StripFormattingNodes(contextAsNode);
  1.2071 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2072 +
  1.2073 +    RemoveBodyAndHead(contextAsNode);
  1.2074 +
  1.2075 +    rv = FindTargetNode(contextAsNode, contextLeaf);
  1.2076 +    if (rv == NS_FOUND_TARGET) {
  1.2077 +      rv = NS_OK;
  1.2078 +    }
  1.2079 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2080 +  }
  1.2081 +
  1.2082 +  nsCOMPtr<nsIContent> contextLeafAsContent = do_QueryInterface(contextLeaf);
  1.2083 +
  1.2084 +  // create fragment for pasted html
  1.2085 +  nsIAtom* contextAtom;
  1.2086 +  if (contextLeafAsContent) {
  1.2087 +    contextAtom = contextLeafAsContent->Tag();
  1.2088 +    if (contextAtom == nsGkAtoms::html) {
  1.2089 +      contextAtom = nsGkAtoms::body;
  1.2090 +    }
  1.2091 +  } else {
  1.2092 +    contextAtom = nsGkAtoms::body;
  1.2093 +  }
  1.2094 +  rv = ParseFragment(aInputString,
  1.2095 +                     contextAtom,
  1.2096 +                     doc,
  1.2097 +                     outFragNode,
  1.2098 +                     aTrustedInput);
  1.2099 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2100 +  NS_ENSURE_TRUE(*outFragNode, NS_ERROR_FAILURE);
  1.2101 +
  1.2102 +  RemoveBodyAndHead(*outFragNode);
  1.2103 +
  1.2104 +  if (contextAsNode)
  1.2105 +  {
  1.2106 +    // unite the two trees
  1.2107 +    contextLeaf->AppendChild(*outFragNode, getter_AddRefs(junk));
  1.2108 +    *outFragNode = contextAsNode;
  1.2109 +  }
  1.2110 +
  1.2111 +  rv = StripFormattingNodes(*outFragNode, true);
  1.2112 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2113 +
  1.2114 +  // If there was no context, then treat all of the data we did get as the
  1.2115 +  // pasted data.
  1.2116 +  if (contextLeaf)
  1.2117 +    *outEndNode = *outStartNode = contextLeaf;
  1.2118 +  else
  1.2119 +    *outEndNode = *outStartNode = *outFragNode;
  1.2120 +
  1.2121 +  *outStartOffset = 0;
  1.2122 +
  1.2123 +  // get the infoString contents
  1.2124 +  nsAutoString numstr1, numstr2;
  1.2125 +  if (!aInfoStr.IsEmpty())
  1.2126 +  {
  1.2127 +    int32_t sep, num;
  1.2128 +    sep = aInfoStr.FindChar((char16_t)',');
  1.2129 +    numstr1 = Substring(aInfoStr, 0, sep);
  1.2130 +    numstr2 = Substring(aInfoStr, sep+1, aInfoStr.Length() - (sep+1));
  1.2131 +
  1.2132 +    // Move the start and end children.
  1.2133 +    nsresult err;
  1.2134 +    num = numstr1.ToInteger(&err);
  1.2135 +    while (num--)
  1.2136 +    {
  1.2137 +      (*outStartNode)->GetFirstChild(getter_AddRefs(tmp));
  1.2138 +      NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
  1.2139 +      tmp.swap(*outStartNode);
  1.2140 +    }
  1.2141 +
  1.2142 +    num = numstr2.ToInteger(&err);
  1.2143 +    while (num--)
  1.2144 +    {
  1.2145 +      (*outEndNode)->GetLastChild(getter_AddRefs(tmp));
  1.2146 +      NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
  1.2147 +      tmp.swap(*outEndNode);
  1.2148 +    }
  1.2149 +  }
  1.2150 +
  1.2151 +  GetLengthOfDOMNode(*outEndNode, (uint32_t&)*outEndOffset);
  1.2152 +  return NS_OK;
  1.2153 +}
  1.2154 +
  1.2155 +
  1.2156 +nsresult nsHTMLEditor::ParseFragment(const nsAString & aFragStr,
  1.2157 +                                     nsIAtom* aContextLocalName,
  1.2158 +                                     nsIDocument* aTargetDocument,
  1.2159 +                                     nsCOMPtr<nsIDOMNode> *outNode,
  1.2160 +                                     bool aTrustedInput)
  1.2161 +{
  1.2162 +  nsAutoScriptBlockerSuppressNodeRemoved autoBlocker;
  1.2163 +
  1.2164 +  nsRefPtr<DocumentFragment> fragment =
  1.2165 +    new DocumentFragment(aTargetDocument->NodeInfoManager());
  1.2166 +  nsresult rv = nsContentUtils::ParseFragmentHTML(aFragStr,
  1.2167 +                                                  fragment,
  1.2168 +                                                  aContextLocalName ?
  1.2169 +                                                    aContextLocalName : nsGkAtoms::body,
  1.2170 +                                                    kNameSpaceID_XHTML,
  1.2171 +                                                  false,
  1.2172 +                                                  true);
  1.2173 +  if (!aTrustedInput) {
  1.2174 +    nsTreeSanitizer sanitizer(aContextLocalName ?
  1.2175 +                              nsIParserUtils::SanitizerAllowStyle :
  1.2176 +                              nsIParserUtils::SanitizerAllowComments);
  1.2177 +    sanitizer.Sanitize(fragment);
  1.2178 +  }
  1.2179 +  *outNode = fragment.forget();
  1.2180 +  return rv;
  1.2181 +}
  1.2182 +
  1.2183 +nsresult nsHTMLEditor::CreateListOfNodesToPaste(nsIDOMNode  *aFragmentAsNode,
  1.2184 +                                                nsCOMArray<nsIDOMNode>& outNodeList,
  1.2185 +                                                nsIDOMNode *aStartNode,
  1.2186 +                                                int32_t aStartOffset,
  1.2187 +                                                nsIDOMNode *aEndNode,
  1.2188 +                                                int32_t aEndOffset)
  1.2189 +{
  1.2190 +  NS_ENSURE_TRUE(aFragmentAsNode, NS_ERROR_NULL_POINTER);
  1.2191 +
  1.2192 +  nsresult rv;
  1.2193 +
  1.2194 +  // if no info was provided about the boundary between context and stream,
  1.2195 +  // then assume all is stream.
  1.2196 +  if (!aStartNode)
  1.2197 +  {
  1.2198 +    int32_t fragLen;
  1.2199 +    rv = GetLengthOfDOMNode(aFragmentAsNode, (uint32_t&)fragLen);
  1.2200 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2201 +
  1.2202 +    aStartNode = aFragmentAsNode;
  1.2203 +    aStartOffset = 0;
  1.2204 +    aEndNode = aFragmentAsNode;
  1.2205 +    aEndOffset = fragLen;
  1.2206 +  }
  1.2207 +
  1.2208 +  nsRefPtr<nsRange> docFragRange;
  1.2209 +  rv = nsRange::CreateRange(aStartNode, aStartOffset, aEndNode, aEndOffset, getter_AddRefs(docFragRange));
  1.2210 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2211 +
  1.2212 +  // now use a subtree iterator over the range to create a list of nodes
  1.2213 +  nsTrivialFunctor functor;
  1.2214 +  nsDOMSubtreeIterator iter;
  1.2215 +  rv = iter.Init(docFragRange);
  1.2216 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2217 +
  1.2218 +  return iter.AppendList(functor, outNodeList);
  1.2219 +}
  1.2220 +
  1.2221 +nsresult
  1.2222 +nsHTMLEditor::GetListAndTableParents(bool aEnd,
  1.2223 +                                     nsCOMArray<nsIDOMNode>& aListOfNodes,
  1.2224 +                                     nsCOMArray<nsIDOMNode>& outArray)
  1.2225 +{
  1.2226 +  int32_t listCount = aListOfNodes.Count();
  1.2227 +  NS_ENSURE_TRUE(listCount > 0, NS_ERROR_FAILURE);  // no empty lists, please
  1.2228 +
  1.2229 +  // build up list of parents of first (or last) node in list
  1.2230 +  // that are either lists, or tables.
  1.2231 +  int32_t idx = 0;
  1.2232 +  if (aEnd) idx = listCount-1;
  1.2233 +
  1.2234 +  nsCOMPtr<nsIDOMNode> pNode = aListOfNodes[idx];
  1.2235 +  while (pNode)
  1.2236 +  {
  1.2237 +    if (nsHTMLEditUtils::IsList(pNode) || nsHTMLEditUtils::IsTable(pNode))
  1.2238 +    {
  1.2239 +      NS_ENSURE_TRUE(outArray.AppendObject(pNode), NS_ERROR_FAILURE);
  1.2240 +    }
  1.2241 +    nsCOMPtr<nsIDOMNode> parent;
  1.2242 +    pNode->GetParentNode(getter_AddRefs(parent));
  1.2243 +    pNode = parent;
  1.2244 +  }
  1.2245 +  return NS_OK;
  1.2246 +}
  1.2247 +
  1.2248 +nsresult
  1.2249 +nsHTMLEditor::DiscoverPartialListsAndTables(nsCOMArray<nsIDOMNode>& aPasteNodes,
  1.2250 +                                            nsCOMArray<nsIDOMNode>& aListsAndTables,
  1.2251 +                                            int32_t *outHighWaterMark)
  1.2252 +{
  1.2253 +  NS_ENSURE_TRUE(outHighWaterMark, NS_ERROR_NULL_POINTER);
  1.2254 +  
  1.2255 +  *outHighWaterMark = -1;
  1.2256 +  int32_t listAndTableParents = aListsAndTables.Count();
  1.2257 +  
  1.2258 +  // scan insertion list for table elements (other than table).
  1.2259 +  int32_t listCount = aPasteNodes.Count();
  1.2260 +  int32_t j;  
  1.2261 +  for (j=0; j<listCount; j++)
  1.2262 +  {
  1.2263 +    nsCOMPtr<nsIDOMNode> curNode = aPasteNodes[j];
  1.2264 +
  1.2265 +    NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE);
  1.2266 +    if (nsHTMLEditUtils::IsTableElement(curNode) && !nsHTMLEditUtils::IsTable(curNode))
  1.2267 +    {
  1.2268 +      nsCOMPtr<nsIDOMNode> theTable = GetTableParent(curNode);
  1.2269 +      if (theTable)
  1.2270 +      {
  1.2271 +        int32_t indexT = aListsAndTables.IndexOf(theTable);
  1.2272 +        if (indexT >= 0)
  1.2273 +        {
  1.2274 +          *outHighWaterMark = indexT;
  1.2275 +          if (*outHighWaterMark == listAndTableParents-1) break;
  1.2276 +        }
  1.2277 +        else
  1.2278 +        {
  1.2279 +          break;
  1.2280 +        }
  1.2281 +      }
  1.2282 +    }
  1.2283 +    if (nsHTMLEditUtils::IsListItem(curNode))
  1.2284 +    {
  1.2285 +      nsCOMPtr<nsIDOMNode> theList = GetListParent(curNode);
  1.2286 +      if (theList)
  1.2287 +      {
  1.2288 +        int32_t indexL = aListsAndTables.IndexOf(theList);
  1.2289 +        if (indexL >= 0)
  1.2290 +        {
  1.2291 +          *outHighWaterMark = indexL;
  1.2292 +          if (*outHighWaterMark == listAndTableParents-1) break;
  1.2293 +        }
  1.2294 +        else
  1.2295 +        {
  1.2296 +          break;
  1.2297 +        }
  1.2298 +      }
  1.2299 +    }
  1.2300 +  }
  1.2301 +  return NS_OK;
  1.2302 +}
  1.2303 +
  1.2304 +nsresult
  1.2305 +nsHTMLEditor::ScanForListAndTableStructure( bool aEnd,
  1.2306 +                                            nsCOMArray<nsIDOMNode>& aNodes,
  1.2307 +                                            nsIDOMNode *aListOrTable,
  1.2308 +                                            nsCOMPtr<nsIDOMNode> *outReplaceNode)
  1.2309 +{
  1.2310 +  NS_ENSURE_TRUE(aListOrTable, NS_ERROR_NULL_POINTER);
  1.2311 +  NS_ENSURE_TRUE(outReplaceNode, NS_ERROR_NULL_POINTER);
  1.2312 +
  1.2313 +  *outReplaceNode = 0;
  1.2314 +  
  1.2315 +  // look upward from first/last paste node for a piece of this list/table
  1.2316 +  int32_t listCount = aNodes.Count(), idx = 0;
  1.2317 +  if (aEnd) idx = listCount-1;
  1.2318 +  bool bList = nsHTMLEditUtils::IsList(aListOrTable);
  1.2319 +  
  1.2320 +  nsCOMPtr<nsIDOMNode>  pNode = aNodes[idx];
  1.2321 +  nsCOMPtr<nsIDOMNode>  originalNode = pNode;
  1.2322 +  while (pNode)
  1.2323 +  {
  1.2324 +    if ((bList && nsHTMLEditUtils::IsListItem(pNode)) ||
  1.2325 +        (!bList && (nsHTMLEditUtils::IsTableElement(pNode) && !nsHTMLEditUtils::IsTable(pNode))))
  1.2326 +    {
  1.2327 +      nsCOMPtr<nsIDOMNode> structureNode;
  1.2328 +      if (bList) structureNode = GetListParent(pNode);
  1.2329 +      else structureNode = GetTableParent(pNode);
  1.2330 +      if (structureNode == aListOrTable)
  1.2331 +      {
  1.2332 +        if (bList)
  1.2333 +          *outReplaceNode = structureNode;
  1.2334 +        else
  1.2335 +          *outReplaceNode = pNode;
  1.2336 +        break;
  1.2337 +      }
  1.2338 +    }
  1.2339 +    nsCOMPtr<nsIDOMNode> parent;
  1.2340 +    pNode->GetParentNode(getter_AddRefs(parent));
  1.2341 +    pNode = parent;
  1.2342 +  }
  1.2343 +  return NS_OK;
  1.2344 +}
  1.2345 +
  1.2346 +nsresult
  1.2347 +nsHTMLEditor::ReplaceOrphanedStructure(bool aEnd,
  1.2348 +                                       nsCOMArray<nsIDOMNode>& aNodeArray,
  1.2349 +                                       nsCOMArray<nsIDOMNode>& aListAndTableArray,
  1.2350 +                                       int32_t aHighWaterMark)
  1.2351 +{
  1.2352 +  nsCOMPtr<nsIDOMNode> curNode = aListAndTableArray[aHighWaterMark];
  1.2353 +  NS_ENSURE_TRUE(curNode, NS_ERROR_NULL_POINTER);
  1.2354 +
  1.2355 +  nsCOMPtr<nsIDOMNode> replaceNode, originalNode;
  1.2356 +
  1.2357 +  // find substructure of list or table that must be included in paste.
  1.2358 +  nsresult rv = ScanForListAndTableStructure(aEnd, aNodeArray,
  1.2359 +                                 curNode, address_of(replaceNode));
  1.2360 +  NS_ENSURE_SUCCESS(rv, rv);
  1.2361 +
  1.2362 +  // if we found substructure, paste it instead of its descendants
  1.2363 +  if (replaceNode)
  1.2364 +  {
  1.2365 +    // postprocess list to remove any descendants of this node
  1.2366 +    // so that we don't insert them twice.
  1.2367 +    nsCOMPtr<nsIDOMNode> endpoint;
  1.2368 +    do
  1.2369 +    {
  1.2370 +      endpoint = GetArrayEndpoint(aEnd, aNodeArray);
  1.2371 +      if (!endpoint) break;
  1.2372 +      if (nsEditorUtils::IsDescendantOf(endpoint, replaceNode))
  1.2373 +        aNodeArray.RemoveObject(endpoint);
  1.2374 +      else
  1.2375 +        break;
  1.2376 +    } while(endpoint);
  1.2377 +
  1.2378 +    // now replace the removed nodes with the structural parent
  1.2379 +    if (aEnd) aNodeArray.AppendObject(replaceNode);
  1.2380 +    else aNodeArray.InsertObjectAt(replaceNode, 0);
  1.2381 +  }
  1.2382 +  return NS_OK;
  1.2383 +}
  1.2384 +
  1.2385 +nsIDOMNode* nsHTMLEditor::GetArrayEndpoint(bool aEnd,
  1.2386 +                                           nsCOMArray<nsIDOMNode>& aNodeArray)
  1.2387 +{
  1.2388 +  int32_t listCount = aNodeArray.Count();
  1.2389 +  if (listCount <= 0) {
  1.2390 +    return nullptr;
  1.2391 +  }
  1.2392 +
  1.2393 +  if (aEnd) {
  1.2394 +    return aNodeArray[listCount-1];
  1.2395 +  }
  1.2396 +
  1.2397 +  return aNodeArray[0];
  1.2398 +}

mercurial