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/DataTransfer.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/DocumentInlines.h"
17 #include "mozilla/dom/Selection.h"
19 #include "nsAString.h"
21 #include "nsContentUtils.h"
24 #include "nsIClipboard.h"
25 #include "nsIContent.h"
26 #include "nsIDragService.h"
27 #include "nsIDragSession.h"
28 #include "nsIPrincipal.h"
29 #include "nsIFormControl.h"
30 #include "nsISupportsPrimitives.h"
31 #include "nsITransferable.h"
32 #include "nsIVariant.h"
33 #include "nsLiteralString.h"
35 #include "nsServiceManagerUtils.h"
44 nsresult
TextEditor::InsertTextFromTransferable(
45 nsITransferable
* aTransferable
) {
46 MOZ_ASSERT(IsEditActionDataAvailable());
47 MOZ_ASSERT(IsTextEditor());
49 nsAutoCString bestFlavor
;
50 nsCOMPtr
<nsISupports
> genericDataObj
;
51 nsresult rv
= aTransferable
->GetAnyTransferData(
52 bestFlavor
, getter_AddRefs(genericDataObj
));
55 "nsITransferable::GetAnyDataTransferData() failed, but ignored");
56 if (NS_SUCCEEDED(rv
) && (bestFlavor
.EqualsLiteral(kTextMime
) ||
57 bestFlavor
.EqualsLiteral(kMozTextInternal
))) {
58 AutoTransactionsConserveSelection
dontChangeMySelection(*this);
60 nsAutoString stuffToPaste
;
61 if (nsCOMPtr
<nsISupportsString
> text
= do_QueryInterface(genericDataObj
)) {
62 text
->GetData(stuffToPaste
);
64 MOZ_ASSERT(GetEditAction() == EditAction::ePaste
);
65 // Use native line breaks for compatibility with Chrome.
66 // XXX Although, somebody has already converted native line breaks to
68 UpdateEditActionData(stuffToPaste
);
70 nsresult rv
= MaybeDispatchBeforeInputEvent();
73 rv
== NS_ERROR_EDITOR_ACTION_CANCELED
,
74 "EditorBase::MaybeDispatchBeforeInputEvent() failed");
78 if (!stuffToPaste
.IsEmpty()) {
79 // Sanitize possible carriage returns in the string to be inserted
80 nsContentUtils::PlatformToDOMLineBreaks(stuffToPaste
);
82 AutoPlaceholderBatch
treatAsOneTransaction(
83 *this, ScrollSelectionIntoView::Yes
, __FUNCTION__
);
85 InsertTextAsSubAction(stuffToPaste
, SelectionHandling::Delete
);
87 NS_WARNING("EditorBase::InsertTextAsSubAction() failed");
93 // Try to scroll the selection into view if the paste/drop succeeded
94 rv
= ScrollSelectionFocusIntoView();
95 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
96 "EditorBase::ScrollSelectionFocusIntoView() failed");
100 nsresult
TextEditor::InsertDroppedDataTransferAsAction(
101 AutoEditActionDataSetter
& aEditActionData
, DataTransfer
& aDataTransfer
,
102 const EditorDOMPoint
& aDroppedAt
, nsIPrincipal
* aSourcePrincipal
) {
103 MOZ_ASSERT(aEditActionData
.GetEditAction() == EditAction::eDrop
);
104 MOZ_ASSERT(GetEditAction() == EditAction::eDrop
);
105 MOZ_ASSERT(aDroppedAt
.IsSet());
106 MOZ_ASSERT(aDataTransfer
.MozItemCount() > 0);
108 uint32_t numItems
= aDataTransfer
.MozItemCount();
109 AutoTArray
<nsString
, 5> textArray
;
110 textArray
.SetCapacity(numItems
);
111 uint32_t textLength
= 0;
112 for (uint32_t i
= 0; i
< numItems
; ++i
) {
113 nsCOMPtr
<nsIVariant
> data
;
114 aDataTransfer
.GetDataAtNoSecurityCheck(u
"text/plain"_ns
, i
,
115 getter_AddRefs(data
));
119 // Use nsString to avoid copying its storage to textArray.
121 data
->GetAsAString(insertText
);
122 if (insertText
.IsEmpty()) {
125 textArray
.AppendElement(insertText
);
126 textLength
+= insertText
.Length();
128 // Use nsString to avoid copying its storage to aEditActionData.
130 data
.SetCapacity(textLength
);
131 // Join the text array from end to start because we insert each items
132 // in the aDataTransfer at same point from start to end. Although I
133 // don't know whether this is intentional behavior.
134 for (nsString
& text
: Reversed(textArray
)) {
137 // Use native line breaks for compatibility with Chrome.
138 // XXX Although, somebody has already converted native line breaks to
140 aEditActionData
.SetData(data
);
142 nsresult rv
= aEditActionData
.MaybeDispatchBeforeInputEvent();
144 NS_WARNING_ASSERTION(rv
== NS_ERROR_EDITOR_ACTION_CANCELED
,
145 "MaybeDispatchBeforeInputEvent() failed");
149 // Then, insert the text. Note that we shouldn't need to walk the array
150 // anymore because nobody should listen to mutation events of anonymous
151 // text node in <input>/<textarea>.
152 nsContentUtils::PlatformToDOMLineBreaks(data
);
153 rv
= InsertTextAt(data
, aDroppedAt
, DeleteSelectedContent::No
);
154 if (NS_WARN_IF(Destroyed())) {
155 return NS_ERROR_EDITOR_DESTROYED
;
157 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
158 "EditorBase::InsertTextAt(DeleteSelectedContent::No) "
159 "failed, but ignored");
163 nsresult
TextEditor::HandlePaste(AutoEditActionDataSetter
& aEditActionData
,
164 int32_t aClipboardType
) {
165 if (NS_WARN_IF(!GetDocument())) {
169 // The data will be initialized in InsertTextFromTransferable() if we're not
170 // an HTMLEditor. Therefore, we cannot dispatch "beforeinput" here.
172 // Get Clipboard Service
174 nsCOMPtr
<nsIClipboard
> clipboard
=
175 do_GetService("@mozilla.org/widget/clipboard;1", &rv
);
177 NS_WARNING("Failed to get nsIClipboard service");
181 // Get the nsITransferable interface for getting the data from the clipboard
182 Result
<nsCOMPtr
<nsITransferable
>, nsresult
> maybeTransferable
=
183 EditorUtils::CreateTransferableForPlainText(*GetDocument());
184 if (maybeTransferable
.isErr()) {
185 NS_WARNING("EditorUtils::CreateTransferableForPlainText() failed");
186 return maybeTransferable
.unwrapErr();
188 nsCOMPtr
<nsITransferable
> transferable(maybeTransferable
.unwrap());
189 if (NS_WARN_IF(!transferable
)) {
191 "EditorUtils::CreateTransferableForPlainText() returned nullptr, but "
193 return NS_OK
; // XXX Why?
195 // Get the Data from the clipboard.
196 auto* windowContext
= GetDocument()->GetWindowContext();
197 if (!windowContext
) {
198 NS_WARNING("Editor didn't have document window context");
199 return NS_ERROR_FAILURE
;
201 rv
= clipboard
->GetData(transferable
, aClipboardType
, windowContext
);
204 NS_WARNING("nsIClipboard::GetData() failed, but ignored");
205 return NS_OK
; // XXX Why?
207 // XXX Why don't we check this first?
208 if (!IsModifiable()) {
211 rv
= InsertTextFromTransferable(transferable
);
212 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
213 "TextEditor::InsertTextFromTransferable() failed");
217 nsresult
TextEditor::HandlePasteTransferable(
218 AutoEditActionDataSetter
& aEditActionData
, nsITransferable
& aTransferable
) {
219 if (!IsModifiable()) {
223 // FYI: The data of beforeinput will be initialized in
224 // InsertTextFromTransferable(). Therefore, here does not touch
226 nsresult rv
= InsertTextFromTransferable(&aTransferable
);
227 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
228 "TextEditor::InsertTextFromTransferable() failed");
232 bool TextEditor::CanPaste(int32_t aClipboardType
) const {
233 if (AreClipboardCommandsUnconditionallyEnabled()) {
237 // can't paste if readonly
238 if (!IsModifiable()) {
243 nsCOMPtr
<nsIClipboard
> clipboard(
244 do_GetService("@mozilla.org/widget/clipboard;1", &rv
));
246 NS_WARNING("Failed to get nsIClipboard service");
250 // the flavors that we can deal with
251 AutoTArray
<nsCString
, 1> textEditorFlavors
= {nsDependentCString(kTextMime
)};
254 rv
= clipboard
->HasDataMatchingFlavors(textEditorFlavors
, aClipboardType
,
256 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
257 "nsIClipboard::HasDataMatchingFlavors() failed");
258 return NS_SUCCEEDED(rv
) && haveFlavors
;
261 bool TextEditor::CanPasteTransferable(nsITransferable
* aTransferable
) {
262 // can't paste if readonly
263 if (!IsModifiable()) {
267 // If |aTransferable| is null, assume that a paste will succeed.
268 if (!aTransferable
) {
272 nsCOMPtr
<nsISupports
> data
;
273 nsresult rv
= aTransferable
->GetTransferData(kTextMime
, getter_AddRefs(data
));
274 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
275 "nsITransferable::GetTransferData(kTextMime) failed");
276 return NS_SUCCEEDED(rv
) && data
;
279 } // namespace mozilla