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