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 +}