Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=2 sw=2 et tw=78: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/HTMLEditor.h"
8
9
#include <string.h>
10
11
#include "HTMLEditUtils.h"
12
#include "InternetCiter.h"
13
#include "WSRunObject.h"
14
#include "mozilla/dom/Comment.h"
15
#include "mozilla/dom/DataTransfer.h"
16
#include "mozilla/dom/DocumentFragment.h"
17
#include "mozilla/dom/DOMException.h"
18
#include "mozilla/dom/DOMStringList.h"
19
#include "mozilla/dom/FileReader.h"
20
#include "mozilla/dom/Selection.h"
21
#include "mozilla/dom/WorkerRef.h"
22
#include "mozilla/ArrayUtils.h"
23
#include "mozilla/Base64.h"
24
#include "mozilla/BasicEvents.h"
25
#include "mozilla/EditAction.h"
26
#include "mozilla/EditorDOMPoint.h"
27
#include "mozilla/EditorUtils.h"
28
#include "mozilla/OwningNonNull.h"
29
#include "mozilla/Preferences.h"
30
#include "mozilla/SelectionState.h"
31
#include "nsAString.h"
32
#include "nsCOMPtr.h"
33
#include "nsCRTGlue.h" // for CRLF
34
#include "nsComponentManagerUtils.h"
35
#include "nsIScriptError.h"
36
#include "nsContentUtils.h"
37
#include "nsDebug.h"
38
#include "nsDependentSubstring.h"
39
#include "nsError.h"
40
#include "nsGkAtoms.h"
41
#include "nsIClipboard.h"
42
#include "nsIContent.h"
43
#include "mozilla/dom/Document.h"
44
#include "nsIDocumentEncoder.h"
45
#include "nsIFile.h"
46
#include "nsIInputStream.h"
47
#include "nsNameSpaceManager.h"
48
#include "nsINode.h"
49
#include "nsIParserUtils.h"
50
#include "nsIPrincipal.h"
51
#include "nsISupportsImpl.h"
52
#include "nsISupportsPrimitives.h"
53
#include "nsISupportsUtils.h"
54
#include "nsITransferable.h"
55
#include "nsIVariant.h"
56
#include "nsLinebreakConverter.h"
57
#include "nsLiteralString.h"
58
#include "nsNetUtil.h"
59
#include "nsRange.h"
60
#include "nsReadableUtils.h"
61
#include "nsServiceManagerUtils.h"
62
#include "nsStreamUtils.h"
63
#include "nsString.h"
64
#include "nsStringFwd.h"
65
#include "nsStringIterator.h"
66
#include "nsTreeSanitizer.h"
67
#include "nsXPCOM.h"
68
#include "nscore.h"
69
#include "nsContentUtils.h"
70
#include "nsQueryObject.h"
71
72
class nsAtom;
73
class nsILoadContext;
74
class nsISupports;
75
76
namespace mozilla {
77
78
using namespace dom;
79
80
#define kInsertCookie "_moz_Insert Here_moz_"
81
82
// some little helpers
83
static bool FindIntegerAfterString(const char* aLeadingString, nsCString& aCStr,
84
int32_t& foundNumber);
85
static nsresult RemoveFragComments(nsCString& theStr);
86
static void RemoveBodyAndHead(nsINode& aNode);
87
static nsresult FindTargetNode(nsINode* aStart, nsCOMPtr<nsINode>& aResult);
88
89
nsresult HTMLEditor::LoadHTML(const nsAString& aInputString) {
90
MOZ_ASSERT(IsEditActionDataAvailable());
91
92
if (NS_WARN_IF(!mInitSucceeded)) {
93
return NS_ERROR_NOT_INITIALIZED;
94
}
95
96
// force IME commit; set up rules sniffing and batching
97
CommitComposition();
98
if (NS_WARN_IF(Destroyed())) {
99
return NS_ERROR_EDITOR_DESTROYED;
100
}
101
102
AutoPlaceholderBatch treatAsOneTransaction(*this);
103
IgnoredErrorResult ignoredError;
104
AutoEditSubActionNotifier startToHandleEditSubAction(
105
*this, EditSubAction::eInsertHTMLSource, nsIEditor::eNext, ignoredError);
106
if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
107
return ignoredError.StealNSResult();
108
}
109
NS_WARNING_ASSERTION(
110
!ignoredError.Failed(),
111
"OnStartToHandleTopLevelEditSubAction() failed, but ignored");
112
113
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
114
if (NS_WARN_IF(NS_FAILED(rv))) {
115
return rv;
116
}
117
118
// Delete Selection, but only if it isn't collapsed, see bug #106269
119
if (!SelectionRefPtr()->IsCollapsed()) {
120
rv = DeleteSelectionAsSubAction(eNone, eStrip);
121
if (NS_WARN_IF(NS_FAILED(rv))) {
122
return rv;
123
}
124
}
125
126
// Get the first range in the selection, for context:
127
RefPtr<nsRange> range = SelectionRefPtr()->GetRangeAt(0);
128
if (NS_WARN_IF(!range)) {
129
return NS_ERROR_FAILURE;
130
}
131
132
// Create fragment for pasted HTML.
133
ErrorResult error;
134
RefPtr<DocumentFragment> documentFragment =
135
range->CreateContextualFragment(aInputString, error);
136
if (NS_WARN_IF(error.Failed())) {
137
return error.StealNSResult();
138
}
139
140
// Put the fragment into the document at start of selection.
141
EditorDOMPoint pointToInsert(range->StartRef());
142
// XXX We need to make pointToInsert store offset for keeping traditional
143
// behavior since using only child node to pointing insertion point
144
// changes the behavior when inserted child is moved by mutation
145
// observer. We need to investigate what we should do here.
146
Unused << pointToInsert.Offset();
147
for (nsCOMPtr<nsIContent> contentToInsert = documentFragment->GetFirstChild();
148
contentToInsert; contentToInsert = documentFragment->GetFirstChild()) {
149
rv = InsertNodeWithTransaction(*contentToInsert, pointToInsert);
150
if (NS_WARN_IF(NS_FAILED(rv))) {
151
return rv;
152
}
153
// XXX If the inserted node has been moved by mutation observer,
154
// incrementing offset will cause odd result. Next new node
155
// will be inserted after existing node and the offset will be
156
// overflown from the container node.
157
pointToInsert.Set(pointToInsert.GetContainer(), pointToInsert.Offset() + 1);
158
if (NS_WARN_IF(!pointToInsert.Offset())) {
159
// Append the remaining children to the container if offset is
160
// overflown.
161
pointToInsert.SetToEndOf(pointToInsert.GetContainer());
162
}
163
}
164
165
return NS_OK;
166
}
167
168
NS_IMETHODIMP
169
HTMLEditor::InsertHTML(const nsAString& aInString) {
170
nsresult rv = InsertHTMLAsAction(aInString);
171
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to insert HTML");
172
return rv;
173
}
174
175
nsresult HTMLEditor::InsertHTMLAsAction(const nsAString& aInString,
176
nsIPrincipal* aPrincipal) {
177
AutoEditActionDataSetter editActionData(*this, EditAction::eInsertHTML,
178
aPrincipal);
179
nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
180
if (rv == NS_ERROR_EDITOR_ACTION_CANCELED || NS_WARN_IF(NS_FAILED(rv))) {
181
return EditorBase::ToGenericNSResult(rv);
182
}
183
184
rv = DoInsertHTMLWithContext(aInString, EmptyString(), EmptyString(),
185
EmptyString(), nullptr, EditorDOMPoint(), true,
186
true, false);
187
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "DoInsertHTMLWithContext() failed");
188
return EditorBase::ToGenericNSResult(rv);
189
}
190
191
nsresult HTMLEditor::DoInsertHTMLWithContext(
192
const nsAString& aInputString, const nsAString& aContextStr,
193
const nsAString& aInfoStr, const nsAString& aFlavor, Document* aSourceDoc,
194
const EditorDOMPoint& aPointToInsert, bool aDoDeleteSelection,
195
bool aTrustedInput, bool aClearStyle) {
196
MOZ_ASSERT(IsEditActionDataAvailable());
197
198
if (NS_WARN_IF(!mInitSucceeded)) {
199
return NS_ERROR_NOT_INITIALIZED;
200
}
201
202
// force IME commit; set up rules sniffing and batching
203
CommitComposition();
204
AutoPlaceholderBatch treatAsOneTransaction(*this);
205
IgnoredErrorResult ignoredError;
206
AutoEditSubActionNotifier startToHandleEditSubAction(
207
*this, EditSubAction::ePasteHTMLContent, nsIEditor::eNext, ignoredError);
208
if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
209
return ignoredError.StealNSResult();
210
}
211
NS_WARNING_ASSERTION(
212
!ignoredError.Failed(),
213
"OnStartToHandleTopLevelEditSubAction() failed, but ignored");
214
ignoredError.SuppressException();
215
216
// create a dom document fragment that represents the structure to paste
217
nsCOMPtr<nsINode> fragmentAsNode, streamStartParent, streamEndParent;
218
int32_t streamStartOffset = 0, streamEndOffset = 0;
219
220
nsresult rv = CreateDOMFragmentFromPaste(
221
aInputString, aContextStr, aInfoStr, address_of(fragmentAsNode),
222
address_of(streamStartParent), address_of(streamEndParent),
223
&streamStartOffset, &streamEndOffset, aTrustedInput);
224
if (NS_WARN_IF(NS_FAILED(rv))) {
225
return rv;
226
}
227
228
// if we have a destination / target node, we want to insert there
229
// rather than in place of the selection
230
// ignore aDoDeleteSelection here if aPointToInsert is not set since deletion
231
// will also occur later; this block is intended to cover the various
232
// scenarios where we are dropping in an editor (and may want to delete
233
// the selection before collapsing the selection in the new destination)
234
if (aPointToInsert.IsSet()) {
235
rv = PrepareToInsertContent(aPointToInsert, aDoDeleteSelection);
236
if (NS_WARN_IF(NS_FAILED(rv))) {
237
return rv;
238
}
239
}
240
241
// we need to recalculate various things based on potentially new offsets
242
// this is work to be completed at a later date (probably by jfrancis)
243
244
// make a list of what nodes in docFrag we need to move
245
AutoTArray<OwningNonNull<nsINode>, 64> nodeList;
246
CreateListOfNodesToPaste(*fragmentAsNode->AsDocumentFragment(), nodeList,
247
streamStartParent, streamStartOffset,
248
streamEndParent, streamEndOffset);
249
250
if (nodeList.IsEmpty()) {
251
// We aren't inserting anything, but if aDoDeleteSelection is set, we do
252
// want to delete everything.
253
// XXX What will this do? We've already called DeleteSelectionAsSubAtion()
254
// above if insertion point is specified.
255
if (aDoDeleteSelection) {
256
nsresult rv = DeleteSelectionAsSubAction(eNone, eStrip);
257
if (NS_WARN_IF(NS_FAILED(rv))) {
258
return rv;
259
}
260
}
261
return NS_OK;
262
}
263
264
// Are there any table elements in the list?
265
// check for table cell selection mode
266
bool cellSelectionMode = false;
267
RefPtr<Element> cellElement = GetFirstSelectedTableCellElement(ignoredError);
268
if (cellElement) {
269
cellSelectionMode = true;
270
}
271
272
if (cellSelectionMode) {
273
// do we have table content to paste? If so, we want to delete
274
// the selected table cells and replace with new table elements;
275
// but if not we want to delete _contents_ of cells and replace
276
// with non-table elements. Use cellSelectionMode bool to
277
// indicate results.
278
if (!HTMLEditUtils::IsTableElement(nodeList[0])) {
279
cellSelectionMode = false;
280
}
281
}
282
283
if (!cellSelectionMode) {
284
rv = DeleteSelectionAndPrepareToCreateNode();
285
if (NS_WARN_IF(NS_FAILED(rv))) {
286
return rv;
287
}
288
289
if (aClearStyle) {
290
// pasting does not inherit local inline styles
291
EditResult result = ClearStyleAt(
292
EditorDOMPoint(SelectionRefPtr()->AnchorRef()), nullptr, nullptr);
293
if (NS_WARN_IF(result.Failed())) {
294
return result.Rv();
295
}
296
}
297
} else {
298
// Delete whole cells: we will replace with new table content.
299
300
// Braces for artificial block to scope AutoSelectionRestorer.
301
// Save current selection since DeleteTableCellWithTransaction() perturbs
302
// it.
303
{
304
AutoSelectionRestorer restoreSelectionLater(*this);
305
rv = DeleteTableCellWithTransaction(1);
306
if (NS_WARN_IF(NS_FAILED(rv))) {
307
return rv;
308
}
309
}
310
// collapse selection to beginning of deleted table content
311
IgnoredErrorResult ignoredError;
312
SelectionRefPtr()->CollapseToStart(ignoredError);
313
NS_WARNING_ASSERTION(!ignoredError.Failed(),
314
"Failed to collapse Selection to start");
315
}
316
317
// XXX Why don't we test these first?
318
if (IsReadonly() || IsDisabled()) {
319
return NS_OK;
320
}
321
322
EditActionResult result = CanHandleHTMLEditSubAction();
323
if (NS_WARN_IF(result.Failed()) || result.Canceled()) {
324
return result.Rv();
325
}
326
327
UndefineCaretBidiLevel();
328
329
rv = EnsureNoPaddingBRElementForEmptyEditor();
330
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
331
return NS_ERROR_EDITOR_DESTROYED;
332
}
333
NS_WARNING_ASSERTION(
334
NS_SUCCEEDED(rv),
335
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
336
337
if (NS_SUCCEEDED(rv) && SelectionRefPtr()->IsCollapsed()) {
338
nsresult rv = EnsureCaretNotAfterPaddingBRElement();
339
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
340
return NS_ERROR_EDITOR_DESTROYED;
341
}
342
NS_WARNING_ASSERTION(
343
NS_SUCCEEDED(rv),
344
"EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
345
if (NS_SUCCEEDED(rv)) {
346
nsresult rv = PrepareInlineStylesForCaret();
347
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
348
return NS_ERROR_EDITOR_DESTROYED;
349
}
350
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
351
"PrepareInlineStylesForCaret() failed, but ignored");
352
}
353
}
354
355
// Adjust position based on the first node we are going to insert.
356
EditorDOMPoint pointToInsert = GetBetterInsertionPointFor(
357
nodeList[0], EditorBase::GetStartPoint(*SelectionRefPtr()));
358
if (NS_WARN_IF(!pointToInsert.IsSet())) {
359
return NS_ERROR_FAILURE;
360
}
361
362
// Remove invisible `<br>` element at the point because if there is a `<br>`
363
// element at end of what we paste, it will make the existing invisible
364
// `<br>` element visible.
365
WSRunObject wsObj(this, pointToInsert);
366
if (wsObj.mEndReasonNode &&
367
wsObj.mEndReasonNode->IsHTMLElement(nsGkAtoms::br) &&
368
!IsVisibleBRElement(wsObj.mEndReasonNode)) {
369
AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
370
rv = DeleteNodeWithTransaction(MOZ_KnownLive(*wsObj.mEndReasonNode));
371
if (NS_WARN_IF(NS_FAILED(rv))) {
372
return rv;
373
}
374
}
375
376
bool insertionPointWasInLink = !!GetLinkElement(pointToInsert.GetContainer());
377
378
if (pointToInsert.IsInTextNode()) {
379
SplitNodeResult splitNodeResult = SplitNodeDeepWithTransaction(
380
MOZ_KnownLive(*pointToInsert.GetContainerAsContent()), pointToInsert,
381
SplitAtEdges::eAllowToCreateEmptyContainer);
382
if (NS_WARN_IF(splitNodeResult.Failed())) {
383
return splitNodeResult.Rv();
384
}
385
pointToInsert = splitNodeResult.SplitPoint();
386
if (NS_WARN_IF(!pointToInsert.IsSet())) {
387
return NS_ERROR_FAILURE;
388
}
389
}
390
391
// build up list of parents of first node in list that are either
392
// lists or tables. First examine front of paste node list.
393
AutoTArray<OwningNonNull<Element>, 4> startListAndTableArray;
394
GetListAndTableParents(StartOrEnd::start, nodeList, startListAndTableArray);
395
if (!startListAndTableArray.IsEmpty()) {
396
int32_t highWaterMark =
397
DiscoverPartialListsAndTables(nodeList, startListAndTableArray);
398
// if we have pieces of tables or lists to be inserted, let's force the
399
// paste to deal with table elements right away, so that it doesn't orphan
400
// some table or list contents outside the table or list.
401
if (highWaterMark >= 0) {
402
ReplaceOrphanedStructure(StartOrEnd::start, nodeList,
403
startListAndTableArray, highWaterMark);
404
}
405
}
406
407
// Now go through the same process again for the end of the paste node list.
408
AutoTArray<OwningNonNull<Element>, 4> endListAndTableArray;
409
GetListAndTableParents(StartOrEnd::end, nodeList, endListAndTableArray);
410
if (!endListAndTableArray.IsEmpty()) {
411
int32_t highWaterMark =
412
DiscoverPartialListsAndTables(nodeList, endListAndTableArray);
413
// don't orphan partial list or table structure
414
if (highWaterMark >= 0) {
415
ReplaceOrphanedStructure(StartOrEnd::end, nodeList, endListAndTableArray,
416
highWaterMark);
417
}
418
}
419
420
MOZ_ASSERT(pointToInsert.GetContainer()->GetChildAt_Deprecated(
421
pointToInsert.Offset()) == pointToInsert.GetChild());
422
423
// Loop over the node list and paste the nodes:
424
nsCOMPtr<nsINode> parentBlock =
425
IsBlockNode(pointToInsert.GetContainer())
426
? pointToInsert.GetContainer()
427
: GetBlockNodeParent(pointToInsert.GetContainer());
428
nsCOMPtr<nsIContent> lastInsertedContent;
429
nsCOMPtr<nsINode> insertedContextParent;
430
for (OwningNonNull<nsINode>& curNode : nodeList) {
431
if (NS_WARN_IF(curNode == fragmentAsNode) ||
432
NS_WARN_IF(curNode->IsHTMLElement(nsGkAtoms::body))) {
433
return NS_ERROR_FAILURE;
434
}
435
436
if (insertedContextParent) {
437
// If we had to insert something higher up in the paste hierarchy,
438
// we want to skip any further paste nodes that descend from that.
439
// Else we will paste twice.
440
// XXX This check may be really expensive. Cannot we check whether
441
// the node's `ownerDocument` is the `fragmentAsNode` or not?
442
// XXX If curNode was moved to outside of insertedContextParent by
443
// mutation event listeners, we will anyway duplicate it.
444
if (EditorUtils::IsDescendantOf(*curNode, *insertedContextParent)) {
445
continue;
446
}
447
}
448
449
// If a `<table>` or `<tr>` element on the clipboard, and pasting it into
450
// a `<table>` or `<tr>` element, insert only the appropriate children
451
// instead.
452
bool inserted = false;
453
if (HTMLEditUtils::IsTableRow(curNode) &&
454
HTMLEditUtils::IsTableRow(pointToInsert.GetContainer()) &&
455
(HTMLEditUtils::IsTable(curNode) ||
456
HTMLEditUtils::IsTable(pointToInsert.GetContainer()))) {
457
// Move children of current node to the insertion point.
458
for (nsCOMPtr<nsIContent> firstChild = curNode->GetFirstChild();
459
firstChild; firstChild = curNode->GetFirstChild()) {
460
EditorDOMPoint insertedPoint =
461
InsertNodeIntoProperAncestorWithTransaction(
462
*firstChild, pointToInsert,
463
SplitAtEdges::eDoNotCreateEmptyContainer);
464
if (NS_WARN_IF(!insertedPoint.IsSet())) {
465
break;
466
}
467
inserted = true;
468
lastInsertedContent = firstChild;
469
pointToInsert = insertedPoint;
470
DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
471
NS_WARNING_ASSERTION(advanced,
472
"Failed to advance offset from inserted point");
473
}
474
}
475
// If a list element on the clipboard, and pasting it into a list or
476
// list item element, insert the appropriate children instead. I.e.,
477
// merge the list elements instead of pasting as a sublist.
478
else if (HTMLEditUtils::IsList(curNode) &&
479
(HTMLEditUtils::IsList(pointToInsert.GetContainer()) ||
480
HTMLEditUtils::IsListItem(pointToInsert.GetContainer()))) {
481
for (nsCOMPtr<nsIContent> firstChild = curNode->GetFirstChild();
482
firstChild; firstChild = curNode->GetFirstChild()) {
483
if (HTMLEditUtils::IsListItem(firstChild) ||
484
HTMLEditUtils::IsList(firstChild)) {
485
// If we're pasting into empty list item, we should remove it
486
// and past current node into the parent list directly.
487
// XXX This creates invalid structure if current list item element
488
// is not proper child of the parent element, or current node
489
// is a list element.
490
if (HTMLEditUtils::IsListItem(pointToInsert.GetContainer())) {
491
bool isEmpty;
492
rv = IsEmptyNode(pointToInsert.GetContainer(), &isEmpty, true);
493
if (NS_SUCCEEDED(rv) && isEmpty) {
494
NS_WARNING_ASSERTION(
495
pointToInsert.GetContainer()->GetParentNode(),
496
"Insertion point is out of the DOM tree");
497
if (pointToInsert.GetContainer()->GetParentNode()) {
498
pointToInsert.Set(pointToInsert.GetContainer());
499
AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
500
DeleteNodeWithTransaction(
501
MOZ_KnownLive(*pointToInsert.GetChild()));
502
}
503
}
504
}
505
EditorDOMPoint insertedPoint =
506
InsertNodeIntoProperAncestorWithTransaction(
507
*firstChild, pointToInsert,
508
SplitAtEdges::eDoNotCreateEmptyContainer);
509
if (NS_WARN_IF(!insertedPoint.IsSet())) {
510
break;
511
}
512
513
inserted = true;
514
lastInsertedContent = firstChild;
515
pointToInsert = insertedPoint;
516
DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
517
NS_WARNING_ASSERTION(advanced,
518
"Failed to advance offset from inserted point");
519
}
520
// If the child of current node is not list item nor list element,
521
// we should remove it from the DOM tree.
522
else {
523
AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
524
ErrorResult error;
525
curNode->RemoveChild(*firstChild, error);
526
if (NS_WARN_IF(error.Failed())) {
527
error.SuppressException();
528
}
529
}
530
}
531
}
532
// If pasting into a `<pre>` element and current node is a `<pre>` element,
533
// move only its children.
534
else if (parentBlock && HTMLEditUtils::IsPre(parentBlock) &&
535
HTMLEditUtils::IsPre(curNode)) {
536
// Check for pre's going into pre's.
537
for (nsCOMPtr<nsIContent> firstChild = curNode->GetFirstChild();
538
firstChild; firstChild = curNode->GetFirstChild()) {
539
EditorDOMPoint insertedPoint =
540
InsertNodeIntoProperAncestorWithTransaction(
541
*firstChild, pointToInsert,
542
SplitAtEdges::eDoNotCreateEmptyContainer);
543
if (NS_WARN_IF(!insertedPoint.IsSet())) {
544
break;
545
}
546
547
inserted = true;
548
lastInsertedContent = firstChild;
549
pointToInsert = insertedPoint;
550
DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
551
NS_WARNING_ASSERTION(advanced,
552
"Failed to advance offset from inserted point");
553
}
554
}
555
556
// If we haven't inserted current node nor its children, move current node
557
// to the insertion point.
558
// XXX Checking `rv` here is odd since it's set only by `IsEmpty()` in
559
// the case of list and list item handling. And may be it may have
560
// been set not current time. So, I think that we should ignore it.
561
if (!inserted || NS_FAILED(rv)) {
562
EditorDOMPoint insertedPoint =
563
InsertNodeIntoProperAncestorWithTransaction(
564
MOZ_KnownLive(*curNode->AsContent()), pointToInsert,
565
SplitAtEdges::eDoNotCreateEmptyContainer);
566
if (insertedPoint.IsSet()) {
567
lastInsertedContent = curNode->AsContent();
568
pointToInsert = insertedPoint;
569
}
570
571
// Assume failure means no legal parent in the document hierarchy,
572
// try again with the parent of curNode in the paste hierarchy.
573
for (nsCOMPtr<nsIContent> content =
574
curNode->IsContent() ? curNode->AsContent() : nullptr;
575
content && !insertedPoint.IsSet(); content = content->GetParent()) {
576
if (NS_WARN_IF(!content->GetParent()) ||
577
NS_WARN_IF(content->GetParent()->IsHTMLElement(nsGkAtoms::body))) {
578
continue;
579
}
580
nsCOMPtr<nsINode> oldParent = content->GetParentNode();
581
insertedPoint = InsertNodeIntoProperAncestorWithTransaction(
582
MOZ_KnownLive(*content->GetParent()), pointToInsert,
583
SplitAtEdges::eDoNotCreateEmptyContainer);
584
if (insertedPoint.IsSet()) {
585
insertedContextParent = oldParent;
586
pointToInsert = insertedPoint;
587
}
588
}
589
}
590
if (lastInsertedContent) {
591
pointToInsert.Set(lastInsertedContent);
592
DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
593
NS_WARNING_ASSERTION(advanced,
594
"Failed to advance offset from inserted point");
595
}
596
}
597
598
if (!lastInsertedContent) {
599
return NS_OK;
600
}
601
602
// Now collapse the selection to the end of what we just inserted.
603
EditorDOMPoint pointToPutCaret;
604
605
// but don't cross tables
606
nsINode* containerNode = nullptr;
607
if (!HTMLEditUtils::IsTable(lastInsertedContent)) {
608
containerNode = GetLastEditableLeaf(*lastInsertedContent);
609
Element* mostAncestorTableElement = nullptr;
610
for (nsINode* parentNode = containerNode;
611
parentNode && parentNode != lastInsertedContent;
612
parentNode = parentNode->GetParentNode()) {
613
if (HTMLEditUtils::IsTable(parentNode)) {
614
mostAncestorTableElement = parentNode->AsElement();
615
}
616
}
617
// If we're in table elements, we should put caret into the most ancestor
618
// table element.
619
if (mostAncestorTableElement) {
620
containerNode = mostAncestorTableElement;
621
}
622
}
623
// If we are not in table elements, we should put caret in the last inserted
624
// node.
625
if (!containerNode) {
626
containerNode = lastInsertedContent;
627
}
628
629
// If the container is a text node or a container element except `<table>`
630
// element, put caret a end of it.
631
if (EditorBase::IsTextNode(containerNode) ||
632
(IsContainer(containerNode) && !HTMLEditUtils::IsTable(containerNode))) {
633
pointToPutCaret.SetToEndOf(containerNode);
634
}
635
// Otherwise, i.e., it's an atomic element, `<table>` element or data node,
636
// put caret after it.
637
else {
638
pointToPutCaret.Set(containerNode);
639
DebugOnly<bool> advanced = pointToPutCaret.AdvanceOffset();
640
NS_WARNING_ASSERTION(advanced, "Failed to advance offset from found node");
641
}
642
643
// Make sure we don't end up with selection collapsed after an invisible
644
// `<br>` element.
645
WSRunObject wsRunObj(this, pointToPutCaret);
646
WSType visType;
647
wsRunObj.PriorVisibleNode(pointToPutCaret, &visType);
648
if (visType == WSType::br && !IsVisibleBRElement(wsRunObj.mStartReasonNode)) {
649
WSRunObject wsRunObj2(this, EditorDOMPoint(wsRunObj.mStartReasonNode));
650
nsCOMPtr<nsINode> visibleNode;
651
int32_t visibleNodeOffset;
652
wsRunObj2.PriorVisibleNode(pointToPutCaret, address_of(visibleNode),
653
&visibleNodeOffset, &visType);
654
if (visType == WSType::text || visType == WSType::normalWS) {
655
pointToPutCaret.Set(visibleNode, visibleNodeOffset);
656
} else if (visType == WSType::special) {
657
pointToPutCaret.Set(wsRunObj2.mStartReasonNode);
658
DebugOnly<bool> advanced = pointToPutCaret.AdvanceOffset();
659
NS_WARNING_ASSERTION(advanced,
660
"Failed to advance offset from found object");
661
}
662
}
663
DebugOnly<nsresult> rvIgnored =
664
SelectionRefPtr()->Collapse(pointToPutCaret.ToRawRangeBoundary());
665
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
666
"Selection::Collapse() failed, but ignored");
667
668
// If we didn't start from an `<a href>` element, we should not keep
669
// caret in the link to make users type something outside the link.
670
if (insertionPointWasInLink) {
671
return NS_OK;
672
}
673
RefPtr<Element> linkElement = GetLinkElement(pointToPutCaret.GetContainer());
674
if (!linkElement) {
675
return NS_OK;
676
}
677
// The reason why do that instead of just moving caret after it is, the
678
// link might have ended in an invisible `<br>` element. If so, the code
679
// above just placed selection inside that. So we need to split it instead.
680
// XXX Sounds like that it's not really expensive comparing with the reason
681
// to use SplitNodeDeepWithTransaction() here.
682
SplitNodeResult splitLinkResult = SplitNodeDeepWithTransaction(
683
*linkElement, pointToPutCaret, SplitAtEdges::eDoNotCreateEmptyContainer);
684
NS_WARNING_ASSERTION(splitLinkResult.Succeeded(),
685
"SplitNodeDeepWithTransaction() failed, but ignored");
686
if (splitLinkResult.GetPreviousNode()) {
687
EditorRawDOMPoint afterLeftLink(splitLinkResult.GetPreviousNode());
688
if (afterLeftLink.AdvanceOffset()) {
689
DebugOnly<nsresult> rvIgnored =
690
SelectionRefPtr()->Collapse(afterLeftLink);
691
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
692
"Selection::Collapse() failed, but ignored");
693
}
694
}
695
return NS_OK;
696
}
697
698
// static
699
Element* HTMLEditor::GetLinkElement(nsINode* aNode) {
700
if (NS_WARN_IF(!aNode)) {
701
return nullptr;
702
}
703
nsINode* node = aNode;
704
while (node) {
705
if (HTMLEditUtils::IsLink(node)) {
706
return node->AsElement();
707
}
708
node = node->GetParentNode();
709
}
710
return nullptr;
711
}
712
713
nsresult HTMLEditor::StripFormattingNodes(nsIContent& aNode, bool aListOnly) {
714
if (aNode.TextIsOnlyWhitespace()) {
715
nsCOMPtr<nsINode> parent = aNode.GetParentNode();
716
if (parent) {
717
if (!aListOnly || HTMLEditUtils::IsList(parent)) {
718
ErrorResult rv;
719
parent->RemoveChild(aNode, rv);
720
return rv.StealNSResult();
721
}
722
return NS_OK;
723
}
724
}
725
726
if (!aNode.IsHTMLElement(nsGkAtoms::pre)) {
727
nsCOMPtr<nsIContent> child = aNode.GetLastChild();
728
while (child) {
729
nsCOMPtr<nsIContent> previous = child->GetPreviousSibling();
730
nsresult rv = StripFormattingNodes(*child, aListOnly);
731
NS_ENSURE_SUCCESS(rv, rv);
732
child = previous.forget();
733
}
734
}
735
return NS_OK;
736
}
737
738
nsresult HTMLEditor::PrepareTransferable(nsITransferable** aTransferable) {
739
return NS_OK;
740
}
741
742
nsresult HTMLEditor::PrepareHTMLTransferable(nsITransferable** aTransferable) {
743
// Create generic Transferable for getting the data
744
nsresult rv =
745
CallCreateInstance("@mozilla.org/widget/transferable;1", aTransferable);
746
NS_ENSURE_SUCCESS(rv, rv);
747
748
// Get the nsITransferable interface for getting the data from the clipboard
749
if (aTransferable) {
750
RefPtr<Document> destdoc = GetDocument();
751
nsILoadContext* loadContext = destdoc ? destdoc->GetLoadContext() : nullptr;
752
(*aTransferable)->Init(loadContext);
753
754
// Create the desired DataFlavor for the type of data
755
// we want to get out of the transferable
756
// This should only happen in html editors, not plaintext
757
if (!IsPlaintextEditor()) {
758
(*aTransferable)->AddDataFlavor(kNativeHTMLMime);
759
(*aTransferable)->AddDataFlavor(kHTMLMime);
760
(*aTransferable)->AddDataFlavor(kFileMime);
761
762
switch (Preferences::GetInt("clipboard.paste_image_type", 1)) {
763
case 0: // prefer JPEG over PNG over GIF encoding
764
(*aTransferable)->AddDataFlavor(kJPEGImageMime);
765
(*aTransferable)->AddDataFlavor(kJPGImageMime);
766
(*aTransferable)->AddDataFlavor(kPNGImageMime);
767
(*aTransferable)->AddDataFlavor(kGIFImageMime);
768
break;
769
case 1: // prefer PNG over JPEG over GIF encoding (default)
770
default:
771
(*aTransferable)->AddDataFlavor(kPNGImageMime);
772
(*aTransferable)->AddDataFlavor(kJPEGImageMime);
773
(*aTransferable)->AddDataFlavor(kJPGImageMime);
774
(*aTransferable)->AddDataFlavor(kGIFImageMime);
775
break;
776
case 2: // prefer GIF over JPEG over PNG encoding
777
(*aTransferable)->AddDataFlavor(kGIFImageMime);
778
(*aTransferable)->AddDataFlavor(kJPEGImageMime);
779
(*aTransferable)->AddDataFlavor(kJPGImageMime);
780
(*aTransferable)->AddDataFlavor(kPNGImageMime);
781
break;
782
}
783
}
784
(*aTransferable)->AddDataFlavor(kUnicodeMime);
785
(*aTransferable)->AddDataFlavor(kMozTextInternal);
786
}
787
788
return NS_OK;
789
}
790
791
bool FindIntegerAfterString(const char* aLeadingString, nsCString& aCStr,
792
int32_t& foundNumber) {
793
// first obtain offsets from cfhtml str
794
int32_t numFront = aCStr.Find(aLeadingString);
795
if (numFront == -1) {
796
return false;
797
}
798
numFront += strlen(aLeadingString);
799
800
int32_t numBack = aCStr.FindCharInSet(CRLF, numFront);
801
if (numBack == -1) {
802
return false;
803
}
804
805
nsAutoCString numStr(Substring(aCStr, numFront, numBack - numFront));
806
nsresult errorCode;
807
foundNumber = numStr.ToInteger(&errorCode);
808
return true;
809
}
810
811
nsresult RemoveFragComments(nsCString& aStr) {
812
// remove the StartFragment/EndFragment comments from the str, if present
813
int32_t startCommentIndx = aStr.Find("<!--StartFragment");
814
if (startCommentIndx >= 0) {
815
int32_t startCommentEnd = aStr.Find("-->", false, startCommentIndx);
816
if (startCommentEnd > startCommentIndx) {
817
aStr.Cut(startCommentIndx, (startCommentEnd + 3) - startCommentIndx);
818
}
819
}
820
int32_t endCommentIndx = aStr.Find("<!--EndFragment");
821
if (endCommentIndx >= 0) {
822
int32_t endCommentEnd = aStr.Find("-->", false, endCommentIndx);
823
if (endCommentEnd > endCommentIndx) {
824
aStr.Cut(endCommentIndx, (endCommentEnd + 3) - endCommentIndx);
825
}
826
}
827
return NS_OK;
828
}
829
830
nsresult HTMLEditor::ParseCFHTML(nsCString& aCfhtml, char16_t** aStuffToPaste,
831
char16_t** aCfcontext) {
832
// First obtain offsets from cfhtml str.
833
int32_t startHTML, endHTML, startFragment, endFragment;
834
if (!FindIntegerAfterString("StartHTML:", aCfhtml, startHTML) ||
835
startHTML < -1) {
836
return NS_ERROR_FAILURE;
837
}
838
if (!FindIntegerAfterString("EndHTML:", aCfhtml, endHTML) || endHTML < -1) {
839
return NS_ERROR_FAILURE;
840
}
841
if (!FindIntegerAfterString("StartFragment:", aCfhtml, startFragment) ||
842
startFragment < 0) {
843
return NS_ERROR_FAILURE;
844
}
845
if (!FindIntegerAfterString("EndFragment:", aCfhtml, endFragment) ||
846
startFragment < 0) {
847
return NS_ERROR_FAILURE;
848
}
849
850
// The StartHTML and EndHTML markers are allowed to be -1 to include
851
// everything.
852
// See Reference: MSDN doc entitled "HTML Clipboard Format"
854
if (startHTML == -1) {
855
startHTML = aCfhtml.Find("<!--StartFragment-->");
856
if (startHTML == -1) {
857
return NS_OK;
858
}
859
}
860
if (endHTML == -1) {
861
const char endFragmentMarker[] = "<!--EndFragment-->";
862
endHTML = aCfhtml.Find(endFragmentMarker);
863
if (endHTML == -1) {
864
return NS_OK;
865
}
866
endHTML += ArrayLength(endFragmentMarker) - 1;
867
}
868
869
// create context string
870
nsAutoCString contextUTF8(
871
Substring(aCfhtml, startHTML, startFragment - startHTML) +
872
NS_LITERAL_CSTRING("<!--" kInsertCookie "-->") +
873
Substring(aCfhtml, endFragment, endHTML - endFragment));
874
875
// validate startFragment
876
// make sure it's not in the middle of a HTML tag
877
// see bug #228879 for more details
878
int32_t curPos = startFragment;
879
while (curPos > startHTML) {
880
if (aCfhtml[curPos] == '>') {
881
// working backwards, the first thing we see is the end of a tag
882
// so StartFragment is good, so do nothing.
883
break;
884
}
885
if (aCfhtml[curPos] == '<') {
886
// if we are at the start, then we want to see the '<'
887
if (curPos != startFragment) {
888
// working backwards, the first thing we see is the start of a tag
889
// so StartFragment is bad, so we need to update it.
890
NS_ERROR(
891
"StartFragment byte count in the clipboard looks bad, see bug "
892
"#228879");
893
startFragment = curPos - 1;
894
}
895
break;
896
}
897
curPos--;
898
}
899
900
// create fragment string
901
nsAutoCString fragmentUTF8(
902
Substring(aCfhtml, startFragment, endFragment - startFragment));
903
904
// remove the StartFragment/EndFragment comments from the fragment, if present
905
RemoveFragComments(fragmentUTF8);
906
907
// remove the StartFragment/EndFragment comments from the context, if present
908
RemoveFragComments(contextUTF8);
909
910
// convert both strings to usc2
911
const nsString& fragUcs2Str = NS_ConvertUTF8toUTF16(fragmentUTF8);
912
const nsString& cntxtUcs2Str = NS_ConvertUTF8toUTF16(contextUTF8);
913
914
// translate platform linebreaks for fragment
915
int32_t oldLengthInChars =
916
fragUcs2Str.Length() + 1; // +1 to include null terminator
917
int32_t newLengthInChars = 0;
918
*aStuffToPaste = nsLinebreakConverter::ConvertUnicharLineBreaks(
919
fragUcs2Str.get(), nsLinebreakConverter::eLinebreakAny,
920
nsLinebreakConverter::eLinebreakContent, oldLengthInChars,
921
&newLengthInChars);
922
NS_ENSURE_TRUE(*aStuffToPaste, NS_ERROR_FAILURE);
923
924
// translate platform linebreaks for context
925
oldLengthInChars =
926
cntxtUcs2Str.Length() + 1; // +1 to include null terminator
927
newLengthInChars = 0;
928
*aCfcontext = nsLinebreakConverter::ConvertUnicharLineBreaks(
929
cntxtUcs2Str.get(), nsLinebreakConverter::eLinebreakAny,
930
nsLinebreakConverter::eLinebreakContent, oldLengthInChars,
931
&newLengthInChars);
932
// it's ok for context to be empty. frag might be whole doc and contain all
933
// its context.
934
935
// we're done!
936
return NS_OK;
937
}
938
939
static nsresult ImgFromData(const nsACString& aType, const nsACString& aData,
940
nsString& aOutput) {
941
nsAutoCString data64;
942
nsresult rv = Base64Encode(aData, data64);
943
NS_ENSURE_SUCCESS(rv, rv);
944
945
aOutput.AssignLiteral("<IMG src=\"data:");
946
AppendUTF8toUTF16(aType, aOutput);
947
aOutput.AppendLiteral(";base64,");
948
if (!AppendASCIItoUTF16(data64, aOutput, fallible_t())) {
949
return NS_ERROR_OUT_OF_MEMORY;
950
}
951
aOutput.AppendLiteral("\" alt=\"\" >");
952
return NS_OK;
953
}
954
955
NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLEditor::BlobReader)
956
957
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLEditor::BlobReader)
958
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBlob)
959
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHTMLEditor)
960
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceDoc)
961
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPointToInsert)
962
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
963
964
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HTMLEditor::BlobReader)
965
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBlob)
966
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHTMLEditor)
967
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceDoc)
968
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPointToInsert)
969
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
970
971
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(HTMLEditor::BlobReader, AddRef)
972
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(HTMLEditor::BlobReader, Release)
973
974
HTMLEditor::BlobReader::BlobReader(BlobImpl* aBlob, HTMLEditor* aHTMLEditor,
975
bool aIsSafe, Document* aSourceDoc,
976
const EditorDOMPoint& aPointToInsert,
977
bool aDoDeleteSelection)
978
: mBlob(aBlob),
979
mHTMLEditor(aHTMLEditor),
980
// "beforeinput" event should've been dispatched before we read blob,
981
// but anyway, we need to clone dataTransfer for "input" event.
982
mDataTransfer(mHTMLEditor->GetInputEventDataTransfer()),
983
mSourceDoc(aSourceDoc),
984
mPointToInsert(aPointToInsert),
985
mEditAction(aHTMLEditor->GetEditAction()),
986
mIsSafe(aIsSafe),
987
mDoDeleteSelection(aDoDeleteSelection),
988
mNeedsToDispatchBeforeInputEvent(
989
!mHTMLEditor->HasTriedToDispatchBeforeInputEvent()) {
990
MOZ_ASSERT(mBlob);
991
MOZ_ASSERT(mHTMLEditor);
992
MOZ_ASSERT(mHTMLEditor->IsEditActionDataAvailable());
993
MOZ_ASSERT(aPointToInsert.IsSet());
994
MOZ_ASSERT(mDataTransfer);
995
996
// Take only offset here since it's our traditional behavior.
997
AutoEditorDOMPointChildInvalidator storeOnlyWithOffset(mPointToInsert);
998
}
999
1000
nsresult HTMLEditor::BlobReader::OnResult(const nsACString& aResult) {
1001
AutoEditActionDataSetter editActionData(*mHTMLEditor, mEditAction);
1002
editActionData.InitializeDataTransfer(mDataTransfer);
1003
if (NS_WARN_IF(!editActionData.CanHandle())) {
1004
return EditorBase::ToGenericNSResult(NS_ERROR_FAILURE);
1005
}
1006
1007
if (NS_WARN_IF(mNeedsToDispatchBeforeInputEvent)) {
1008
nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
1009
if (rv == NS_ERROR_EDITOR_ACTION_CANCELED || NS_WARN_IF(NS_FAILED(rv))) {
1010
return EditorBase::ToGenericNSResult(rv);
1011
}
1012
} else {
1013
editActionData.MarkAsBeforeInputHasBeenDispatched();
1014
}
1015
1016
nsString blobType;
1017
mBlob->GetType(blobType);
1018
1019
// TODO: This does not work well.
1020
// * If the data is not an image file, this inserts <img> element with odd
1021
// data URI (bug 1610220).
1022
// * If the data is valid image file data, an <img> file is inserted with
1023
// data URI, but it's not loaded (bug 1610219).
1024
NS_ConvertUTF16toUTF8 type(blobType);
1025
nsAutoString stuffToPaste;
1026
nsresult rv = ImgFromData(type, aResult, stuffToPaste);
1027
if (NS_WARN_IF(NS_FAILED(rv))) {
1028
return EditorBase::ToGenericNSResult(rv);
1029
}
1030
1031
AutoPlaceholderBatch treatAsOneTransaction(*mHTMLEditor);
1032
RefPtr<Document> sourceDocument(mSourceDoc);
1033
EditorDOMPoint pointToInsert(mPointToInsert);
1034
rv = MOZ_KnownLive(mHTMLEditor)
1035
->DoInsertHTMLWithContext(stuffToPaste, EmptyString(), EmptyString(),
1036
NS_LITERAL_STRING(kFileMime),
1037
sourceDocument, pointToInsert,
1038
mDoDeleteSelection, mIsSafe, false);
1039
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "DoInsertHTMLWithContext() failed");
1040
return EditorBase::ToGenericNSResult(rv);
1041
}
1042
1043
nsresult HTMLEditor::BlobReader::OnError(const nsAString& aError) {
1044
AutoTArray<nsString, 1> error;
1045
error.AppendElement(aError);
1046
nsContentUtils::ReportToConsole(
1047
nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Editor"),
1048
mPointToInsert.GetContainer()->OwnerDoc(),
1049
nsContentUtils::eDOM_PROPERTIES, "EditorFileDropFailed", error);
1050
return NS_OK;
1051
}
1052
1053
class SlurpBlobEventListener final : public nsIDOMEventListener {
1054
public:
1055
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
1056
NS_DECL_CYCLE_COLLECTION_CLASS(SlurpBlobEventListener)
1057
1058
explicit SlurpBlobEventListener(HTMLEditor::BlobReader* aListener)
1059
: mListener(aListener) {}
1060
1061
MOZ_CAN_RUN_SCRIPT
1062
NS_IMETHOD HandleEvent(Event* aEvent) override;
1063
1064
private:
1065
~SlurpBlobEventListener() = default;
1066
1067
RefPtr<HTMLEditor::BlobReader> mListener;
1068
};
1069
1070
NS_IMPL_CYCLE_COLLECTION(SlurpBlobEventListener, mListener)
1071
1072
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SlurpBlobEventListener)
1073
NS_INTERFACE_MAP_ENTRY(nsISupports)
1074
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
1075
NS_INTERFACE_MAP_END
1076
1077
NS_IMPL_CYCLE_COLLECTING_ADDREF(SlurpBlobEventListener)
1078
NS_IMPL_CYCLE_COLLECTING_RELEASE(SlurpBlobEventListener)
1079
1080
NS_IMETHODIMP
1081
SlurpBlobEventListener::HandleEvent(Event* aEvent) {
1082
EventTarget* target = aEvent->GetTarget();
1083
if (!target || !mListener) {
1084
return NS_OK;
1085
}
1086
1087
RefPtr<FileReader> reader = do_QueryObject(target);
1088
if (!reader) {
1089
return NS_OK;
1090
}
1091
1092
EventMessage message = aEvent->WidgetEventPtr()->mMessage;
1093
1094
RefPtr<HTMLEditor::BlobReader> listener(mListener);
1095
if (message == eLoad) {
1096
MOZ_ASSERT(reader->DataFormat() == FileReader::FILE_AS_BINARY);
1097
1098
// The original data has been converted from Latin1 to UTF-16, this just
1099
// undoes that conversion.
1100
listener->OnResult(NS_LossyConvertUTF16toASCII(reader->Result()));
1101
} else if (message == eLoadError) {
1102
nsAutoString errorMessage;
1103
reader->GetError()->GetErrorMessage(errorMessage);
1104
listener->OnError(errorMessage);
1105
}
1106
1107
return NS_OK;
1108
}
1109
1110
// static
1111
nsresult HTMLEditor::SlurpBlob(Blob* aBlob, nsPIDOMWindowOuter* aWindow,
1112
BlobReader* aBlobReader) {
1113
MOZ_ASSERT(aBlob);
1114
MOZ_ASSERT(aWindow);
1115
MOZ_ASSERT(aBlobReader);
1116
1117
nsCOMPtr<nsPIDOMWindowInner> inner = aWindow->GetCurrentInnerWindow();
1118
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(inner);
1119
RefPtr<WeakWorkerRef> workerRef;
1120
RefPtr<FileReader> reader = new FileReader(global, workerRef);
1121
1122
RefPtr<SlurpBlobEventListener> eventListener =
1123
new SlurpBlobEventListener(aBlobReader);
1124
1125
nsresult rv =
1126
reader->AddEventListener(NS_LITERAL_STRING("load"), eventListener, false);
1127
if (NS_WARN_IF(NS_FAILED(rv))) {
1128
return rv;
1129
}
1130
1131
rv = reader->AddEventListener(NS_LITERAL_STRING("error"), eventListener,
1132
false);
1133
if (NS_WARN_IF(NS_FAILED(rv))) {
1134
return rv;
1135
}
1136
1137
ErrorResult result;
1138
reader->ReadAsBinaryString(*aBlob, result);
1139
if (result.Failed()) {
1140
return result.StealNSResult();
1141
}
1142
return NS_OK;
1143
}
1144
1145
nsresult HTMLEditor::InsertObject(const nsACString& aType, nsISupports* aObject,
1146
bool aIsSafe, Document* aSourceDoc,
1147
const EditorDOMPoint& aPointToInsert,
1148
bool aDoDeleteSelection) {
1149
MOZ_ASSERT(IsEditActionDataAvailable());
1150
1151
if (nsCOMPtr<BlobImpl> blob = do_QueryInterface(aObject)) {
1152
RefPtr<BlobReader> br = new BlobReader(blob, this, aIsSafe, aSourceDoc,
1153
aPointToInsert, aDoDeleteSelection);
1154
// XXX This is not guaranteed.
1155
MOZ_ASSERT(aPointToInsert.IsSet());
1156
1157
RefPtr<Blob> domBlob =
1158
Blob::Create(aPointToInsert.GetContainer()->GetOwnerGlobal(), blob);
1159
if (NS_WARN_IF(!domBlob)) {
1160
return NS_ERROR_FAILURE;
1161
}
1162
1163
nsresult rv = SlurpBlob(
1164
domBlob, aPointToInsert.GetContainer()->OwnerDoc()->GetWindow(), br);
1165
if (NS_WARN_IF(NS_FAILED(rv))) {
1166
return rv;
1167
}
1168
return NS_OK;
1169
}
1170
1171
nsAutoCString type(aType);
1172
1173
// Check to see if we can insert an image file
1174
bool insertAsImage = false;
1175
nsCOMPtr<nsIFile> fileObj;
1176
if (type.EqualsLiteral(kFileMime)) {
1177
fileObj = do_QueryInterface(aObject);
1178
if (fileObj) {
1179
// Accept any image type fed to us
1180
if (nsContentUtils::IsFileImage(fileObj, type)) {
1181
insertAsImage = true;
1182
} else {
1183
// Reset type.
1184
type.AssignLiteral(kFileMime);
1185
}
1186
}
1187
}
1188
1189
if (type.EqualsLiteral(kJPEGImageMime) || type.EqualsLiteral(kJPGImageMime) ||
1190
type.EqualsLiteral(kPNGImageMime) || type.EqualsLiteral(kGIFImageMime) ||
1191
insertAsImage) {
1192
nsCString imageData;
1193
if (insertAsImage) {
1194
nsresult rv = nsContentUtils::SlurpFileToString(fileObj, imageData);
1195
if (NS_WARN_IF(NS_FAILED(rv))) {
1196
return rv;
1197
}
1198
} else {
1199
nsCOMPtr<nsIInputStream> imageStream = do_QueryInterface(aObject);
1200
if (NS_WARN_IF(!imageStream)) {
1201
return NS_ERROR_FAILURE;
1202
}
1203
1204
nsresult rv = NS_ConsumeStream(imageStream, UINT32_MAX, imageData);
1205
if (NS_WARN_IF(NS_FAILED(rv))) {
1206
return rv;
1207
}
1208
1209
rv = imageStream->Close();
1210
if (NS_WARN_IF(NS_FAILED(rv))) {
1211
return rv;
1212
}
1213
}
1214
1215
nsAutoString stuffToPaste;
1216
nsresult rv = ImgFromData(type, imageData, stuffToPaste);
1217
if (NS_WARN_IF(NS_FAILED(rv))) {
1218
return rv;
1219
}
1220
1221
AutoPlaceholderBatch treatAsOneTransaction(*this);
1222
rv = DoInsertHTMLWithContext(stuffToPaste, EmptyString(), EmptyString(),
1223
NS_LITERAL_STRING(kFileMime), aSourceDoc,
1224
aPointToInsert, aDoDeleteSelection, aIsSafe,
1225
false);
1226
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1227
"DoInsertHTMLWithContext() failed, but ignored");
1228
}
1229
1230
return NS_OK;
1231
}
1232
1233
static bool GetString(nsISupports* aData, nsAString& aText) {
1234
if (nsCOMPtr<nsISupportsString> str = do_QueryInterface(aData)) {
1235
str->GetData(aText);
1236
return !aText.IsEmpty();
1237
}
1238
1239
return false;
1240
}
1241
1242
static bool GetCString(nsISupports* aData, nsACString& aText) {
1243
if (nsCOMPtr<nsISupportsCString> str = do_QueryInterface(aData)) {
1244
str->GetData(aText);
1245
return !aText.IsEmpty();
1246
}
1247
1248
return false;
1249
}
1250
1251
nsresult HTMLEditor::InsertFromTransferable(nsITransferable* transferable,
1252
Document* aSourceDoc,
1253
const nsAString& aContextStr,
1254
const nsAString& aInfoStr,
1255
bool havePrivateHTMLFlavor,
1256
bool aDoDeleteSelection) {
1257
nsAutoCString bestFlavor;
1258
nsCOMPtr<nsISupports> genericDataObj;
1259
if (NS_SUCCEEDED(transferable->GetAnyTransferData(
1260
bestFlavor, getter_AddRefs(genericDataObj)))) {
1261
AutoTransactionsConserveSelection dontChangeMySelection(*this);
1262
nsAutoString flavor;
1263
CopyASCIItoUTF16(bestFlavor, flavor);
1264
bool isSafe = IsSafeToInsertData(aSourceDoc);
1265
1266
if (bestFlavor.EqualsLiteral(kFileMime) ||
1267
bestFlavor.EqualsLiteral(kJPEGImageMime) ||
1268
bestFlavor.EqualsLiteral(kJPGImageMime) ||
1269
bestFlavor.EqualsLiteral(kPNGImageMime) ||
1270
bestFlavor.EqualsLiteral(kGIFImageMime)) {
1271
nsresult rv = InsertObject(bestFlavor, genericDataObj, isSafe, aSourceDoc,
1272
EditorDOMPoint(), aDoDeleteSelection);
1273
if (NS_WARN_IF(NS_FAILED(rv))) {
1274
return rv;
1275
}
1276
} else if (bestFlavor.EqualsLiteral(kNativeHTMLMime)) {
1277
// note cf_html uses utf8
1278
nsAutoCString cfhtml;
1279
if (GetCString(genericDataObj, cfhtml)) {
1280
// cfselection left emtpy for now.
1281
nsString cfcontext, cffragment, cfselection;
1282
nsresult rv = ParseCFHTML(cfhtml, getter_Copies(cffragment),
1283
getter_Copies(cfcontext));
1284
if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty()) {
1285
AutoPlaceholderBatch treatAsOneTransaction(*this);
1286
// If we have our private HTML flavor, we will only use the fragment
1287
// from the CF_HTML. The rest comes from the clipboard.
1288
if (havePrivateHTMLFlavor) {
1289
rv = DoInsertHTMLWithContext(cffragment, aContextStr, aInfoStr,
1290
flavor, aSourceDoc, EditorDOMPoint(),
1291
aDoDeleteSelection, isSafe);
1292
if (NS_WARN_IF(NS_FAILED(rv))) {
1293
return rv;
1294
}
1295
} else {
1296
rv = DoInsertHTMLWithContext(cffragment, cfcontext, cfselection,
1297
flavor, aSourceDoc, EditorDOMPoint(),
1298
aDoDeleteSelection, isSafe);
1299
if (NS_WARN_IF(NS_FAILED(rv))) {
1300
return rv;
1301
}
1302
}
1303
} else {
1304
// In some platforms (like Linux), the clipboard might return data
1305
// requested for unknown flavors (for example:
1306
// application/x-moz-nativehtml). In this case, treat the data
1307
// to be pasted as mere HTML to get the best chance of pasting it
1308
// correctly.
1309
bestFlavor.AssignLiteral(kHTMLMime);
1310
// Fall through the next case
1311
}
1312
}
1313
}
1314
if (bestFlavor.EqualsLiteral(kHTMLMime) ||
1315
bestFlavor.EqualsLiteral(kUnicodeMime) ||
1316
bestFlavor.EqualsLiteral(kMozTextInternal)) {
1317
nsAutoString stuffToPaste;
1318
if (!GetString(genericDataObj, stuffToPaste)) {
1319
nsAutoCString text;
1320
if (GetCString(genericDataObj, text)) {
1321
CopyUTF8toUTF16(text, stuffToPaste);
1322
}
1323
}
1324
1325
if (!stuffToPaste.IsEmpty()) {
1326
AutoPlaceholderBatch treatAsOneTransaction(*this);
1327
if (bestFlavor.EqualsLiteral(kHTMLMime)) {
1328
nsresult rv = DoInsertHTMLWithContext(
1329
stuffToPaste, aContextStr, aInfoStr, flavor, aSourceDoc,
1330
EditorDOMPoint(), aDoDeleteSelection, isSafe);
1331
if (NS_WARN_IF(NS_FAILED(rv))) {
1332
return rv;
1333
}
1334
} else {
1335
nsresult rv = InsertTextAsSubAction(stuffToPaste);
1336
if (NS_WARN_IF(NS_FAILED(rv))) {
1337
return rv;
1338
}
1339
}
1340
}
1341
}
1342
}
1343
1344
// Try to scroll the selection into view if the paste succeeded
1345
DebugOnly<nsresult> rvIgnored = ScrollSelectionFocusIntoView();
1346
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
1347
"ScrollSelectionFocusIntoView() failed, but ignored");
1348
return NS_OK;
1349
}
1350
1351
static void GetStringFromDataTransfer(DataTransfer* aDataTransfer,
1352
const nsAString& aType, int32_t aIndex,
1353
nsString& aOutputString) {
1354
nsCOMPtr<nsIVariant> variant;
1355
aDataTransfer->GetDataAtNoSecurityCheck(aType, aIndex,
1356
getter_AddRefs(variant));
1357
if (!variant) {
1358
MOZ_ASSERT(aOutputString.IsEmpty());
1359
return;
1360
}
1361
variant->GetAsAString(aOutputString);
1362
nsContentUtils::PlatformToDOMLineBreaks(aOutputString);
1363
}
1364
1365
nsresult HTMLEditor::InsertFromDataTransfer(DataTransfer* aDataTransfer,
1366
int32_t aIndex,
1367
Document* aSourceDoc,
1368
const EditorDOMPoint& aDroppedAt,
1369
bool aDoDeleteSelection) {
1370
MOZ_ASSERT(GetEditAction() == EditAction::eDrop);
1371
MOZ_ASSERT(
1372
mPlaceholderBatch,
1373
"TextEditor::InsertFromDataTransfer() should be called only by OnDrop() "
1374
"and there should've already been placeholder transaction");
1375
MOZ_ASSERT(aDroppedAt.IsSet());
1376
1377
ErrorResult rv;
1378
RefPtr<DOMStringList> types =
1379
aDataTransfer->MozTypesAt(aIndex, CallerType::System, rv);
1380
if (rv.Failed()) {
1381
return rv.StealNSResult();
1382
}
1383
1384
bool hasPrivateHTMLFlavor = types->Contains(NS_LITERAL_STRING(kHTMLContext));
1385
1386
bool isText = IsPlaintextEditor();
1387
bool isSafe = IsSafeToInsertData(aSourceDoc);
1388
1389
uint32_t length = types->Length();
1390
for (uint32_t t = 0; t < length; t++) {
1391
nsAutoString type;
1392
types->Item(t, type);
1393
1394
if (!isText) {
1395
if (type.EqualsLiteral(kFileMime) || type.EqualsLiteral(kJPEGImageMime) ||
1396
type.EqualsLiteral(kJPGImageMime) ||
1397
type.EqualsLiteral(kPNGImageMime) ||
1398
type.EqualsLiteral(kGIFImageMime)) {
1399
nsCOMPtr<nsIVariant> variant;
1400
aDataTransfer->GetDataAtNoSecurityCheck(type, aIndex,
1401
getter_AddRefs(variant));
1402
if (variant) {
1403
nsCOMPtr<nsISupports> object;
1404
variant->GetAsISupports(getter_AddRefs(object));
1405
nsresult rv =
1406
InsertObject(NS_ConvertUTF16toUTF8(type), object, isSafe,
1407
aSourceDoc, aDroppedAt, aDoDeleteSelection);
1408
if (NS_WARN_IF(NS_FAILED(rv))) {
1409
return rv;
1410
}
1411
return NS_OK;
1412
}
1413
} else if (type.EqualsLiteral(kNativeHTMLMime)) {
1414
// Windows only clipboard parsing.
1415
nsAutoString text;
1416
GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
1417
NS_ConvertUTF16toUTF8 cfhtml(text);
1418
1419
nsString cfcontext, cffragment,
1420
cfselection; // cfselection left emtpy for now
1421
1422
nsresult rv = ParseCFHTML(cfhtml, getter_Copies(cffragment),
1423
getter_Copies(cfcontext));
1424
if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty()) {
1425
if (hasPrivateHTMLFlavor) {
1426
// If we have our private HTML flavor, we will only use the fragment
1427
// from the CF_HTML. The rest comes from the clipboard.
1428
nsAutoString contextString, infoString;
1429
GetStringFromDataTransfer(aDataTransfer,
1430
NS_LITERAL_STRING(kHTMLContext), aIndex,
1431
contextString);
1432
GetStringFromDataTransfer(aDataTransfer,
1433
NS_LITERAL_STRING(kHTMLInfo), aIndex,
1434
infoString);
1435
nsresult rv = DoInsertHTMLWithContext(
1436
cffragment, contextString, infoString, type, aSourceDoc,
1437
aDroppedAt, aDoDeleteSelection, isSafe);
1438
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1439
"DoInsertHTMLWithContext() failed");
1440
return rv;
1441
}
1442
nsresult rv = DoInsertHTMLWithContext(
1443
cffragment, cfcontext, cfselection, type, aSourceDoc, aDroppedAt,
1444
aDoDeleteSelection, isSafe);
1445
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1446
"DoInsertHTMLWithContext() failed");
1447
return rv;
1448
}
1449
} else if (type.EqualsLiteral(kHTMLMime)) {
1450
nsAutoString text, contextString, infoString;
1451
GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
1452
GetStringFromDataTransfer(aDataTransfer,
1453
NS_LITERAL_STRING(kHTMLContext), aIndex,
1454
contextString);
1455
GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo),
1456
aIndex, infoString);
1457
if (type.EqualsLiteral(kHTMLMime)) {
1458
nsresult rv = DoInsertHTMLWithContext(text, contextString, infoString,
1459
type, aSourceDoc, aDroppedAt,
1460
aDoDeleteSelection, isSafe);
1461
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1462
"DoInsertHTMLWithContext() failed");
1463
return rv;
1464
}
1465
}
1466
}
1467
1468
if (type.EqualsLiteral(kTextMime) || type.EqualsLiteral(kMozTextInternal)) {
1469
nsAutoString text;
1470
GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
1471
nsresult rv = InsertTextAt(text, aDroppedAt, aDoDeleteSelection);
1472
if (NS_WARN_IF(NS_FAILED(rv))) {
1473
return rv;
1474
}
1475
return NS_OK;
1476
}
1477
}
1478
1479
return NS_OK;
1480
}
1481
1482
bool HTMLEditor::HavePrivateHTMLFlavor(nsIClipboard* aClipboard) {
1483
// check the clipboard for our special kHTMLContext flavor. If that is there,
1484
// we know we have our own internal html format on clipboard.
1485
1486
NS_ENSURE_TRUE(aClipboard, false);
1487
bool bHavePrivateHTMLFlavor = false;
1488
1489
AutoTArray<nsCString, 1> flavArray = {nsDependentCString(kHTMLContext)};
1490
1491
if (NS_SUCCEEDED(aClipboard->HasDataMatchingFlavors(
1492
flavArray, nsIClipboard::kGlobalClipboard,
1493
&bHavePrivateHTMLFlavor))) {
1494
return bHavePrivateHTMLFlavor;
1495
}
1496
1497
return false;
1498
}
1499
1500
nsresult HTMLEditor::PasteInternal(int32_t aClipboardType) {
1501
MOZ_ASSERT(IsEditActionDataAvailable());
1502
1503
// Get Clipboard Service
1504
nsresult rv = NS_OK;
1505
nsCOMPtr<nsIClipboard> clipboard =
1506
do_GetService("@mozilla.org/widget/clipboard;1", &rv);
1507
if (NS_WARN_IF(NS_FAILED(rv))) {
1508
return rv;
1509
}
1510
1511
// Get the nsITransferable interface for getting the data from the clipboard
1512
nsCOMPtr<nsITransferable> transferable;
1513
rv = PrepareHTMLTransferable(getter_AddRefs(transferable));
1514
if (NS_WARN_IF(NS_FAILED(rv))) {
1515
return rv;
1516
}
1517
if (NS_WARN_IF(!transferable)) {
1518
return NS_ERROR_FAILURE;
1519
}
1520
// Get the Data from the clipboard
1521
rv = clipboard->GetData(transferable, aClipboardType);
1522
if (NS_WARN_IF(NS_FAILED(rv))) {
1523
return rv;
1524
}
1525
1526
// XXX Why don't you check this first?
1527
if (!IsModifiable()) {
1528
return NS_OK;
1529
}
1530
1531
// also get additional html copy hints, if present
1532
nsAutoString contextStr, infoStr;
1533
1534
// If we have our internal html flavor on the clipboard, there is special
1535
// context to use instead of cfhtml context.
1536
bool bHavePrivateHTMLFlavor = HavePrivateHTMLFlavor(clipboard);
1537
if (bHavePrivateHTMLFlavor) {
1538
nsCOMPtr<nsITransferable> contextTransferable =
1539
do_CreateInstance("@mozilla.org/widget/transferable;1");
1540
if (NS_WARN_IF(!contextTransferable)) {
1541
return NS_ERROR_FAILURE;
1542
}
1543
contextTransferable->Init(nullptr);
1544
contextTransferable->SetIsPrivateData(transferable->GetIsPrivateData());
1545
contextTransferable->AddDataFlavor(kHTMLContext);
1546
clipboard->GetData(contextTransferable, aClipboardType);
1547
nsCOMPtr<nsISupports> contextDataObj;
1548
rv = contextTransferable->GetTransferData(kHTMLContext,
1549
getter_AddRefs(contextDataObj));
1550
if (NS_SUCCEEDED(rv) && contextDataObj) {
1551
if (nsCOMPtr<nsISupportsString> str = do_QueryInterface(contextDataObj)) {
1552
str->GetData(contextStr);
1553
}
1554
}
1555
1556
nsCOMPtr<nsITransferable> infoTransferable =
1557
do_CreateInstance("@mozilla.org/widget/transferable;1");
1558
if (NS_WARN_IF(!infoTransferable)) {
1559
return NS_ERROR_FAILURE;
1560
}
1561
infoTransferable->Init(nullptr);
1562
contextTransferable->SetIsPrivateData(transferable->GetIsPrivateData());
1563
infoTransferable->AddDataFlavor(kHTMLInfo);
1564
clipboard->GetData(infoTransferable, aClipboardType);
1565
nsCOMPtr<nsISupports> infoDataObj;
1566
rv = infoTransferable->GetTransferData(kHTMLInfo,
1567
getter_AddRefs(infoDataObj));
1568
if (NS_SUCCEEDED(rv) && infoDataObj) {
1569
if (nsCOMPtr<nsISupportsString> str = do_QueryInterface(infoDataObj)) {
1570
str->GetData(infoStr);
1571
}
1572
}
1573
}
1574
1575
rv = InsertFromTransferable(transferable, nullptr, contextStr, infoStr,
1576
bHavePrivateHTMLFlavor, true);
1577
if (NS_WARN_IF(NS_FAILED(rv))) {
1578
return rv;
1579
}
1580
return NS_OK;
1581
}
1582
1583
nsresult HTMLEditor::PasteTransferableAsAction(nsITransferable* aTransferable,
1584
nsIPrincipal* aPrincipal) {
1585
AutoEditActionDataSetter editActionData(*this, EditAction::ePaste,
1586
aPrincipal);
1587
if (NS_WARN_IF(!editActionData.CanHandle())) {
1588
return NS_ERROR_NOT_INITIALIZED;
1589
}
1590
editActionData.InitializeDataTransfer(aTransferable);
1591
1592
// Use an invalid value for the clipboard type as data comes from
1593
// aTransferable and we don't currently implement a way to put that in the
1594
// data transfer yet.
1595
if (!FireClipboardEvent(ePaste, nsIClipboard::kGlobalClipboard)) {
1596
return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
1597
}
1598
1599
// Dispatch "beforeinput" event after "paste" event.
1600
nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
1601
if (rv == NS_ERROR_EDITOR_ACTION_CANCELED || NS_WARN_IF(NS_FAILED(rv))) {
1602
return EditorBase::ToGenericNSResult(rv);
1603
}
1604
1605
nsAutoString contextStr, infoStr;
1606
rv = InsertFromTransferable(aTransferable, nullptr, contextStr, infoStr,
1607
false, true);
1608
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "InsertFromTransferable() failed");
1609
return EditorBase::ToGenericNSResult(rv);
1610
}
1611
1612
/**
1613
* HTML PasteNoFormatting. Ignore any HTML styles and formating in paste source.
1614
*/
1615
NS_IMETHODIMP
1616
HTMLEditor::PasteNoFormatting(int32_t aSelectionType) {
1617
nsresult rv = PasteNoFormattingAsAction(aSelectionType);
1618
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to paste without format");
1619
return rv;
1620
}
1621
1622
nsresult HTMLEditor::PasteNoFormattingAsAction(int32_t aSelectionType,
1623
nsIPrincipal* aPrincipal) {
1624
AutoEditActionDataSetter editActionData(*this, EditAction::ePaste,
1625
aPrincipal);
1626
if (NS_WARN_IF(!editActionData.CanHandle())) {
1627
return NS_ERROR_NOT_INITIALIZED;
1628
}
1629
editActionData.InitializeDataTransferWithClipboard(
1630
SettingDataTransfer::eWithoutFormat, aSelectionType);
1631
1632
if (!FireClipboardEvent(ePasteNoFormatting, aSelectionType)) {
1633
return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
1634
}
1635
1636
// Dispatch "beforeinput" event after "paste" event. And perhaps, before
1637
// committing composition because if pasting is canceled, we don't need to
1638
// commit the active composition.
1639
nsresult rv = editActionData.MaybeDispatchBeforeInputEvent();
1640
if (rv == NS_ERROR_EDITOR_ACTION_CANCELED || NS_WARN_IF(NS_FAILED(rv))) {
1641
return EditorBase::ToGenericNSResult(rv);
1642
}
1643
1644
CommitComposition();
1645
if (NS_WARN_IF(Destroyed())) {
1646
return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
1647
}
1648
1649
// Get Clipboard Service
1650
nsCOMPtr<nsIClipboard> clipboard(
1651
do_GetService("@mozilla.org/widget/clipboard;1", &rv));
1652
if (NS_WARN_IF(NS_FAILED(rv))) {
1653
return rv;
1654
}
1655
1656
// Get the nsITransferable interface for getting the data from the clipboard.
1657
// use TextEditor::PrepareTransferable() to force unicode plaintext data.
1658
nsCOMPtr<nsITransferable> trans;
1659
rv = TextEditor::PrepareTransferable(getter_AddRefs(trans));
1660
if (NS_WARN_IF(NS_FAILED(rv))) {
1661
return EditorBase::ToGenericNSResult(rv);
1662
}
1663
if (!trans) {
1664
return NS_OK;
1665
}
1666
1667
if (!IsModifiable()) {
1668
return NS_OK;
1669
}
1670
1671
// Get the Data from the clipboard
1672
rv = clipboard->GetData(trans, aSelectionType);
1673
if (NS_FAILED(rv)) {
1674
return rv;
1675
}
1676
1677
const nsString& empty = EmptyString();
1678
rv = InsertFromTransferable(trans, nullptr, empty, empty, false, true);
1679
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "InsertFromTransferable() failed");
1680
return EditorBase::ToGenericNSResult(rv);
1681
}
1682
1683
// The following arrays contain the MIME types that we can paste. The arrays
1684
// are used by CanPaste() and CanPasteTransferable() below.
1685
1686
static const char* textEditorFlavors[] = {kUnicodeMime};
1687
static const char* textHtmlEditorFlavors[] = {kUnicodeMime, kHTMLMime,
1688
kJPEGImageMime, kJPGImageMime,
1689
kPNGImageMime, kGIFImageMime};
1690
1691
bool HTMLEditor::CanPaste(int32_t aClipboardType) const {
1692
// Always enable the paste command when inside of a HTML or XHTML document,
1693
// but if the document is chrome, let it control it.
1694
Document* document = GetDocument();
1695
if (document && document->IsHTMLOrXHTML() &&
1696
!nsContentUtils::IsChromeDoc(document)) {
1697
return true;
1698
}
1699
1700
// can't paste if readonly
1701
if (!IsModifiable()) {
1702
return false;
1703
}
1704
1705
nsresult rv;
1706
nsCOMPtr<nsIClipboard> clipboard(
1707
do_GetService("@mozilla.org/widget/clipboard;1", &rv));
1708
if (NS_WARN_IF(NS_FAILED(rv))) {
1709
return false;
1710
}
1711
1712
// Use the flavors depending on the current editor mask
1713
if (IsPlaintextEditor()) {
1714
AutoTArray<nsCString, ArrayLength(textEditorFlavors)> flavors;
1715
flavors.AppendElements<const char*>(Span<const char*>(textEditorFlavors));
1716
bool haveFlavors;
1717
rv = clipboard->HasDataMatchingFlavors(flavors, aClipboardType,
1718
&haveFlavors);
1719
if (NS_WARN_IF(NS_FAILED(rv))) {
1720
return false;
1721
}
1722
return haveFlavors;
1723
}
1724
1725
AutoTArray<nsCString, ArrayLength(textHtmlEditorFlavors)> flavors;
1726
flavors.AppendElements<const char*>(Span<const char*>(textHtmlEditorFlavors));
1727
bool haveFlavors;
1728
rv = clipboard->HasDataMatchingFlavors(flavors, aClipboardType, &haveFlavors);
1729
if (NS_WARN_IF(NS_FAILED(rv))) {
1730
return false;
1731
}
1732
return haveFlavors;
1733
}
1734
1735
bool HTMLEditor::CanPasteTransferable(nsITransferable* aTransferable) {
1736
// can't paste if readonly
1737
if (!IsModifiable()) {
1738
return false;
1739
}
1740
1741
// If |aTransferable| is null, assume that a paste will succeed.
1742
if (!aTransferable) {
1743
return true;
1744
}
1745
1746
// Peek in |aTransferable| to see if it contains a supported MIME type.
1747
1748
// Use the flavors depending on the current editor mask
1749
const char** flavors;
1750
size_t length;
1751