1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "TextEditor.h"
8 #include "EditorUtils.h"
9 #include "HTMLEditor.h"
10 #include "SelectionState.h"
12 #include "mozilla/ArrayUtils.h"
13 #include "mozilla/MouseEvents.h"
14 #include "mozilla/dom/ContentParent.h"
15 #include "mozilla/dom/DataTransfer.h"
16 #include "mozilla/dom/Document.h"
17 #include "mozilla/dom/DocumentInlines.h"
18 #include "mozilla/dom/Selection.h"
20 #include "nsAString.h"
22 #include "nsContentUtils.h"
25 #include "nsIClipboard.h"
26 #include "nsIContent.h"
27 #include "nsIDragService.h"
28 #include "nsIDragSession.h"
29 #include "nsIPrincipal.h"
30 #include "nsIFormControl.h"
31 #include "nsISupportsPrimitives.h"
32 #include "nsITransferable.h"
33 #include "nsIVariant.h"
34 #include "nsLiteralString.h"
36 #include "nsServiceManagerUtils.h"
45 nsresult
TextEditor::InsertTextFromTransferable(
46 nsITransferable
* aTransferable
) {
47 MOZ_ASSERT(IsEditActionDataAvailable());
48 MOZ_ASSERT(IsTextEditor());
50 nsAutoCString bestFlavor
;
51 nsCOMPtr
<nsISupports
> genericDataObj
;
52 nsresult rv
= aTransferable
->GetAnyTransferData(
53 bestFlavor
, getter_AddRefs(genericDataObj
));
56 "nsITransferable::GetAnyDataTransferData() failed, but ignored");
57 if (NS_SUCCEEDED(rv
) && (bestFlavor
.EqualsLiteral(kTextMime
) ||
58 bestFlavor
.EqualsLiteral(kMozTextInternal
))) {
59 AutoTransactionsConserveSelection
dontChangeMySelection(*this);
61 nsAutoString stuffToPaste
;
62 if (nsCOMPtr
<nsISupportsString
> text
= do_QueryInterface(genericDataObj
)) {
63 text
->GetData(stuffToPaste
);
65 MOZ_ASSERT(GetEditAction() == EditAction::ePaste
);
66 // Use native line breaks for compatibility with Chrome.
67 // XXX Although, somebody has already converted native line breaks to
69 UpdateEditActionData(stuffToPaste
);
71 nsresult rv
= MaybeDispatchBeforeInputEvent();
74 rv
== NS_ERROR_EDITOR_ACTION_CANCELED
,
75 "EditorBase::MaybeDispatchBeforeInputEvent() failed");
79 if (!stuffToPaste
.IsEmpty()) {
80 // Sanitize possible carriage returns in the string to be inserted
81 nsContentUtils::PlatformToDOMLineBreaks(stuffToPaste
);
83 AutoPlaceholderBatch
treatAsOneTransaction(
84 *this, ScrollSelectionIntoView::Yes
, __FUNCTION__
);
86 InsertTextAsSubAction(stuffToPaste
, SelectionHandling::Delete
);
88 NS_WARNING("EditorBase::InsertTextAsSubAction() failed");
94 // Try to scroll the selection into view if the paste/drop succeeded
95 rv
= ScrollSelectionFocusIntoView();
96 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
97 "EditorBase::ScrollSelectionFocusIntoView() failed");
101 nsresult
TextEditor::InsertDroppedDataTransferAsAction(
102 AutoEditActionDataSetter
& aEditActionData
, DataTransfer
& aDataTransfer
,
103 const EditorDOMPoint
& aDroppedAt
, nsIPrincipal
* aSourcePrincipal
) {
104 MOZ_ASSERT(aEditActionData
.GetEditAction() == EditAction::eDrop
);
105 MOZ_ASSERT(GetEditAction() == EditAction::eDrop
);
106 MOZ_ASSERT(aDroppedAt
.IsSet());
107 MOZ_ASSERT(aDataTransfer
.MozItemCount() > 0);
109 uint32_t numItems
= aDataTransfer
.MozItemCount();
110 AutoTArray
<nsString
, 5> textArray
;
111 textArray
.SetCapacity(numItems
);
112 uint32_t textLength
= 0;
113 for (uint32_t i
= 0; i
< numItems
; ++i
) {
114 nsCOMPtr
<nsIVariant
> data
;
115 aDataTransfer
.GetDataAtNoSecurityCheck(u
"text/plain"_ns
, i
,
116 getter_AddRefs(data
));
120 // Use nsString to avoid copying its storage to textArray.
122 data
->GetAsAString(insertText
);
123 if (insertText
.IsEmpty()) {
126 textArray
.AppendElement(insertText
);
127 textLength
+= insertText
.Length();
129 // Use nsString to avoid copying its storage to aEditActionData.
131 data
.SetCapacity(textLength
);
132 // Join the text array from end to start because we insert each items
133 // in the aDataTransfer at same point from start to end. Although I
134 // don't know whether this is intentional behavior.
135 for (nsString
& text
: Reversed(textArray
)) {
138 // Use native line breaks for compatibility with Chrome.
139 // XXX Although, somebody has already converted native line breaks to
141 aEditActionData
.SetData(data
);
143 nsresult rv
= aEditActionData
.MaybeDispatchBeforeInputEvent();
145 NS_WARNING_ASSERTION(rv
== NS_ERROR_EDITOR_ACTION_CANCELED
,
146 "MaybeDispatchBeforeInputEvent() failed");
150 // Then, insert the text. Note that we shouldn't need to walk the array
151 // anymore because nobody should listen to mutation events of anonymous
152 // text node in <input>/<textarea>.
153 nsContentUtils::PlatformToDOMLineBreaks(data
);
154 rv
= InsertTextAt(data
, aDroppedAt
, DeleteSelectedContent::No
);
155 if (NS_WARN_IF(Destroyed())) {
156 return NS_ERROR_EDITOR_DESTROYED
;
158 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
159 "EditorBase::InsertTextAt(DeleteSelectedContent::No) "
160 "failed, but ignored");
164 nsresult
TextEditor::HandlePaste(AutoEditActionDataSetter
& aEditActionData
,
165 nsIClipboard::ClipboardType aClipboardType
,
166 DataTransfer
* aDataTransfer
) {
167 if (NS_WARN_IF(!GetDocument())) {
171 // The data will be initialized in InsertTextFromTransferable() if we're not
172 // an HTMLEditor. Therefore, we cannot dispatch "beforeinput" here.
174 // Get Clipboard Service
176 nsCOMPtr
<nsIClipboard
> clipboard
=
177 do_GetService("@mozilla.org/widget/clipboard;1", &rv
);
179 NS_WARNING("Failed to get nsIClipboard service");
183 // Get the nsITransferable interface for getting the data from the clipboard
184 Result
<nsCOMPtr
<nsITransferable
>, nsresult
> maybeTransferable
=
185 EditorUtils::CreateTransferableForPlainText(*GetDocument());
186 if (maybeTransferable
.isErr()) {
187 NS_WARNING("EditorUtils::CreateTransferableForPlainText() failed");
188 return maybeTransferable
.unwrapErr();
190 nsCOMPtr
<nsITransferable
> transferable(maybeTransferable
.unwrap());
191 if (NS_WARN_IF(!transferable
)) {
193 "EditorUtils::CreateTransferableForPlainText() returned nullptr, but "
195 return NS_OK
; // XXX Why?
197 // Get the Data from the clipboard.
198 rv
= GetDataFromDataTransferOrClipboard(aDataTransfer
, transferable
,
201 NS_WARNING("EditorBase::GetDataFromDataTransferOrClipboard() failed");
205 // XXX Why don't we check this first?
206 if (!IsModifiable()) {
209 rv
= InsertTextFromTransferable(transferable
);
210 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
211 "TextEditor::InsertTextFromTransferable() failed");
215 nsresult
TextEditor::HandlePasteTransferable(
216 AutoEditActionDataSetter
& aEditActionData
, nsITransferable
& aTransferable
) {
217 if (!IsModifiable()) {
221 // FYI: The data of beforeinput will be initialized in
222 // InsertTextFromTransferable(). Therefore, here does not touch
224 nsresult rv
= InsertTextFromTransferable(&aTransferable
);
225 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
226 "TextEditor::InsertTextFromTransferable() failed");
230 bool TextEditor::CanPaste(nsIClipboard::ClipboardType aClipboardType
) const {
231 if (AreClipboardCommandsUnconditionallyEnabled()) {
235 // can't paste if readonly
236 if (!IsModifiable()) {
241 nsCOMPtr
<nsIClipboard
> clipboard(
242 do_GetService("@mozilla.org/widget/clipboard;1", &rv
));
244 NS_WARNING("Failed to get nsIClipboard service");
248 // the flavors that we can deal with
249 AutoTArray
<nsCString
, 1> textEditorFlavors
= {nsDependentCString(kTextMime
)};
252 rv
= clipboard
->HasDataMatchingFlavors(textEditorFlavors
, aClipboardType
,
254 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
255 "nsIClipboard::HasDataMatchingFlavors() failed");
256 return NS_SUCCEEDED(rv
) && haveFlavors
;
259 bool TextEditor::CanPasteTransferable(nsITransferable
* aTransferable
) {
260 // can't paste if readonly
261 if (!IsModifiable()) {
265 // If |aTransferable| is null, assume that a paste will succeed.
266 if (!aTransferable
) {
270 nsCOMPtr
<nsISupports
> data
;
271 nsresult rv
= aTransferable
->GetTransferData(kTextMime
, getter_AddRefs(data
));
272 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
273 "nsITransferable::GetTransferData(kTextMime) failed");
274 return NS_SUCCEEDED(rv
) && data
;
277 } // namespace mozilla