Bug 1874684 - Part 10: Replace BigInt with Int128 in RoundNumberToIncrement. r=mgaudet
[gecko.git] / editor / libeditor / TextEditorDataTransfer.cpp
blobae7752843e58d01121f5dc655719fc926f9bf905
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"
20 #include "nsCOMPtr.h"
21 #include "nsContentUtils.h"
22 #include "nsDebug.h"
23 #include "nsError.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"
34 #include "nsRange.h"
35 #include "nsServiceManagerUtils.h"
36 #include "nsString.h"
37 #include "nsXPCOM.h"
38 #include "nscore.h"
40 namespace mozilla {
42 using namespace dom;
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));
53 NS_WARNING_ASSERTION(
54 NS_SUCCEEDED(rv),
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
67 // XP line breaks.
68 UpdateEditActionData(stuffToPaste);
70 nsresult rv = MaybeDispatchBeforeInputEvent();
71 if (NS_FAILED(rv)) {
72 NS_WARNING_ASSERTION(
73 rv == NS_ERROR_EDITOR_ACTION_CANCELED,
74 "EditorBase::MaybeDispatchBeforeInputEvent() failed");
75 return rv;
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__);
84 nsresult rv =
85 InsertTextAsSubAction(stuffToPaste, SelectionHandling::Delete);
86 if (NS_FAILED(rv)) {
87 NS_WARNING("EditorBase::InsertTextAsSubAction() failed");
88 return rv;
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");
97 return rv;
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));
116 if (!data) {
117 continue;
119 // Use nsString to avoid copying its storage to textArray.
120 nsString insertText;
121 data->GetAsAString(insertText);
122 if (insertText.IsEmpty()) {
123 continue;
125 textArray.AppendElement(insertText);
126 textLength += insertText.Length();
128 // Use nsString to avoid copying its storage to aEditActionData.
129 nsString data;
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)) {
135 data.Append(text);
137 // Use native line breaks for compatibility with Chrome.
138 // XXX Although, somebody has already converted native line breaks to
139 // XP line breaks.
140 aEditActionData.SetData(data);
142 nsresult rv = aEditActionData.MaybeDispatchBeforeInputEvent();
143 if (NS_FAILED(rv)) {
144 NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
145 "MaybeDispatchBeforeInputEvent() failed");
146 return rv;
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");
160 return rv;
163 nsresult TextEditor::HandlePaste(AutoEditActionDataSetter& aEditActionData,
164 int32_t aClipboardType) {
165 if (NS_WARN_IF(!GetDocument())) {
166 return NS_OK;
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
173 nsresult rv;
174 nsCOMPtr<nsIClipboard> clipboard =
175 do_GetService("@mozilla.org/widget/clipboard;1", &rv);
176 if (NS_FAILED(rv)) {
177 NS_WARNING("Failed to get nsIClipboard service");
178 return rv;
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)) {
190 NS_WARNING(
191 "EditorUtils::CreateTransferableForPlainText() returned nullptr, but "
192 "ignored");
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);
203 if (NS_FAILED(rv)) {
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()) {
209 return NS_OK;
211 rv = InsertTextFromTransferable(transferable);
212 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
213 "TextEditor::InsertTextFromTransferable() failed");
214 return rv;
217 nsresult TextEditor::HandlePasteTransferable(
218 AutoEditActionDataSetter& aEditActionData, nsITransferable& aTransferable) {
219 if (!IsModifiable()) {
220 return NS_OK;
223 // FYI: The data of beforeinput will be initialized in
224 // InsertTextFromTransferable(). Therefore, here does not touch
225 // aEditActionData.
226 nsresult rv = InsertTextFromTransferable(&aTransferable);
227 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
228 "TextEditor::InsertTextFromTransferable() failed");
229 return rv;
232 bool TextEditor::CanPaste(int32_t aClipboardType) const {
233 if (AreClipboardCommandsUnconditionallyEnabled()) {
234 return true;
237 // can't paste if readonly
238 if (!IsModifiable()) {
239 return false;
242 nsresult rv;
243 nsCOMPtr<nsIClipboard> clipboard(
244 do_GetService("@mozilla.org/widget/clipboard;1", &rv));
245 if (NS_FAILED(rv)) {
246 NS_WARNING("Failed to get nsIClipboard service");
247 return false;
250 // the flavors that we can deal with
251 AutoTArray<nsCString, 1> textEditorFlavors = {nsDependentCString(kTextMime)};
253 bool haveFlavors;
254 rv = clipboard->HasDataMatchingFlavors(textEditorFlavors, aClipboardType,
255 &haveFlavors);
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()) {
264 return false;
267 // If |aTransferable| is null, assume that a paste will succeed.
268 if (!aTransferable) {
269 return true;
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