TextEditorDataTransfer.cpp (9448B)
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/. */ 5 6 #include "TextEditor.h" 7 8 #include "EditorUtils.h" 9 #include "HTMLEditor.h" 10 #include "SelectionState.h" 11 12 #include "mozilla/MouseEvents.h" 13 #include "mozilla/dom/ContentParent.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" 18 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" 39 40 namespace mozilla { 41 42 using namespace dom; 43 44 nsresult TextEditor::InsertTextFromTransferable( 45 nsITransferable* aTransferable) { 46 MOZ_ASSERT(IsEditActionDataAvailable()); 47 MOZ_ASSERT(IsTextEditor()); 48 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); 59 60 nsAutoString stuffToPaste; 61 if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(genericDataObj)) { 62 text->GetData(stuffToPaste); 63 } 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); 69 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; 76 } 77 78 if (!stuffToPaste.IsEmpty()) { 79 // Sanitize possible carriage returns in the string to be inserted 80 nsContentUtils::PlatformToDOMLineBreaks(stuffToPaste); 81 82 AutoPlaceholderBatch treatAsOneTransaction( 83 *this, ScrollSelectionIntoView::Yes, __FUNCTION__); 84 nsresult rv = 85 InsertTextAsSubAction(stuffToPaste, InsertTextFor::NormalText); 86 if (NS_FAILED(rv)) { 87 NS_WARNING("EditorBase::InsertTextAsSubAction() failed"); 88 return rv; 89 } 90 } 91 } 92 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; 98 } 99 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); 107 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; 118 } 119 // Use nsString to avoid copying its storage to textArray. 120 nsString insertText; 121 data->GetAsAString(insertText); 122 if (insertText.IsEmpty()) { 123 continue; 124 } 125 textArray.AppendElement(insertText); 126 textLength += insertText.Length(); 127 } 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); 136 } 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); 141 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; 147 } 148 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; 156 } 157 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 158 "EditorBase::InsertTextAt(DeleteSelectedContent::No) " 159 "failed, but ignored"); 160 return rv; 161 } 162 163 nsresult TextEditor::HandlePaste(AutoEditActionDataSetter& aEditActionData, 164 nsIClipboard::ClipboardType aClipboardType, 165 DataTransfer* aDataTransfer) { 166 if (NS_WARN_IF(!GetDocument())) { 167 return NS_OK; 168 } 169 170 // The data will be initialized in InsertTextFromTransferable() if we're not 171 // an HTMLEditor. Therefore, we cannot dispatch "beforeinput" here. 172 173 // Get Clipboard Service 174 nsresult rv; 175 nsCOMPtr<nsIClipboard> clipboard = 176 do_GetService("@mozilla.org/widget/clipboard;1", &rv); 177 if (NS_FAILED(rv)) { 178 NS_WARNING("Failed to get nsIClipboard service"); 179 return rv; 180 } 181 182 // Get the nsITransferable interface for getting the data from the clipboard 183 Result<nsCOMPtr<nsITransferable>, nsresult> maybeTransferable = 184 EditorUtils::CreateTransferableForPlainText(*GetDocument()); 185 if (maybeTransferable.isErr()) { 186 NS_WARNING("EditorUtils::CreateTransferableForPlainText() failed"); 187 return maybeTransferable.unwrapErr(); 188 } 189 nsCOMPtr<nsITransferable> transferable(maybeTransferable.unwrap()); 190 if (NS_WARN_IF(!transferable)) { 191 NS_WARNING( 192 "EditorUtils::CreateTransferableForPlainText() returned nullptr, but " 193 "ignored"); 194 return NS_OK; // XXX Why? 195 } 196 // Get the Data from the clipboard. 197 rv = GetDataFromDataTransferOrClipboard(aDataTransfer, transferable, 198 aClipboardType); 199 if (NS_FAILED(rv)) { 200 NS_WARNING("EditorBase::GetDataFromDataTransferOrClipboard() failed"); 201 return rv; 202 } 203 204 // XXX Why don't we check this first? 205 if (!IsModifiable()) { 206 return NS_OK; 207 } 208 rv = InsertTextFromTransferable(transferable); 209 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 210 "TextEditor::InsertTextFromTransferable() failed"); 211 return rv; 212 } 213 214 nsresult TextEditor::HandlePasteTransferable( 215 AutoEditActionDataSetter& aEditActionData, nsITransferable& aTransferable) { 216 if (!IsModifiable()) { 217 return NS_OK; 218 } 219 220 // FYI: The data of beforeinput will be initialized in 221 // InsertTextFromTransferable(). Therefore, here does not touch 222 // aEditActionData. 223 nsresult rv = InsertTextFromTransferable(&aTransferable); 224 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 225 "TextEditor::InsertTextFromTransferable() failed"); 226 return rv; 227 } 228 229 bool TextEditor::CanPaste(nsIClipboard::ClipboardType aClipboardType) const { 230 if (AreClipboardCommandsUnconditionallyEnabled()) { 231 return true; 232 } 233 234 // can't paste if readonly 235 if (!IsModifiable()) { 236 return false; 237 } 238 239 nsresult rv; 240 nsCOMPtr<nsIClipboard> clipboard( 241 do_GetService("@mozilla.org/widget/clipboard;1", &rv)); 242 if (NS_FAILED(rv)) { 243 NS_WARNING("Failed to get nsIClipboard service"); 244 return false; 245 } 246 247 // the flavors that we can deal with 248 AutoTArray<nsCString, 1> textEditorFlavors = {nsDependentCString(kTextMime)}; 249 250 bool haveFlavors; 251 rv = clipboard->HasDataMatchingFlavors(textEditorFlavors, aClipboardType, 252 &haveFlavors); 253 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 254 "nsIClipboard::HasDataMatchingFlavors() failed"); 255 return NS_SUCCEEDED(rv) && haveFlavors; 256 } 257 258 bool TextEditor::CanPasteTransferable(nsITransferable* aTransferable) { 259 // can't paste if readonly 260 if (!IsModifiable()) { 261 return false; 262 } 263 264 // If |aTransferable| is null, assume that a paste will succeed. 265 if (!aTransferable) { 266 return true; 267 } 268 269 nsCOMPtr<nsISupports> data; 270 nsresult rv = aTransferable->GetTransferData(kTextMime, getter_AddRefs(data)); 271 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 272 "nsITransferable::GetTransferData(kTextMime) failed"); 273 return NS_SUCCEEDED(rv) && data; 274 } 275 276 } // namespace mozilla