Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "nsCopySupport.h" |
michael@0 | 7 | #include "nsIDocumentEncoder.h" |
michael@0 | 8 | #include "nsISupports.h" |
michael@0 | 9 | #include "nsIContent.h" |
michael@0 | 10 | #include "nsIComponentManager.h" |
michael@0 | 11 | #include "nsIServiceManager.h" |
michael@0 | 12 | #include "nsIClipboard.h" |
michael@0 | 13 | #include "nsISelection.h" |
michael@0 | 14 | #include "nsWidgetsCID.h" |
michael@0 | 15 | #include "nsXPCOM.h" |
michael@0 | 16 | #include "nsISupportsPrimitives.h" |
michael@0 | 17 | #include "nsIDOMRange.h" |
michael@0 | 18 | #include "nsRange.h" |
michael@0 | 19 | #include "imgIContainer.h" |
michael@0 | 20 | #include "nsIPresShell.h" |
michael@0 | 21 | #include "nsFocusManager.h" |
michael@0 | 22 | #include "mozilla/dom/DataTransfer.h" |
michael@0 | 23 | |
michael@0 | 24 | #include "nsIDocShell.h" |
michael@0 | 25 | #include "nsIContentViewerEdit.h" |
michael@0 | 26 | #include "nsIClipboardDragDropHooks.h" |
michael@0 | 27 | #include "nsIClipboardDragDropHookList.h" |
michael@0 | 28 | #include "nsIClipboardHelper.h" |
michael@0 | 29 | #include "nsISelectionController.h" |
michael@0 | 30 | |
michael@0 | 31 | #include "nsPIDOMWindow.h" |
michael@0 | 32 | #include "nsIDocument.h" |
michael@0 | 33 | #include "nsIDOMNode.h" |
michael@0 | 34 | #include "nsIDOMElement.h" |
michael@0 | 35 | #include "nsIDOMDocument.h" |
michael@0 | 36 | #include "nsIHTMLDocument.h" |
michael@0 | 37 | #include "nsGkAtoms.h" |
michael@0 | 38 | #include "nsIFrame.h" |
michael@0 | 39 | #include "nsIURI.h" |
michael@0 | 40 | #include "nsISimpleEnumerator.h" |
michael@0 | 41 | |
michael@0 | 42 | // image copy stuff |
michael@0 | 43 | #include "nsIImageLoadingContent.h" |
michael@0 | 44 | #include "nsIInterfaceRequestorUtils.h" |
michael@0 | 45 | #include "nsContentUtils.h" |
michael@0 | 46 | #include "nsContentCID.h" |
michael@0 | 47 | |
michael@0 | 48 | #include "mozilla/ContentEvents.h" |
michael@0 | 49 | #include "mozilla/dom/Element.h" |
michael@0 | 50 | #include "mozilla/EventDispatcher.h" |
michael@0 | 51 | #include "mozilla/Preferences.h" |
michael@0 | 52 | #include "mozilla/dom/Selection.h" |
michael@0 | 53 | |
michael@0 | 54 | using namespace mozilla; |
michael@0 | 55 | using namespace mozilla::dom; |
michael@0 | 56 | |
michael@0 | 57 | nsresult NS_NewDomSelection(nsISelection **aDomSelection); |
michael@0 | 58 | |
michael@0 | 59 | static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); |
michael@0 | 60 | static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); |
michael@0 | 61 | static NS_DEFINE_CID(kHTMLConverterCID, NS_HTMLFORMATCONVERTER_CID); |
michael@0 | 62 | |
michael@0 | 63 | // copy string data onto the transferable |
michael@0 | 64 | static nsresult AppendString(nsITransferable *aTransferable, |
michael@0 | 65 | const nsAString& aString, |
michael@0 | 66 | const char* aFlavor); |
michael@0 | 67 | |
michael@0 | 68 | // copy HTML node data |
michael@0 | 69 | static nsresult AppendDOMNode(nsITransferable *aTransferable, |
michael@0 | 70 | nsINode* aDOMNode); |
michael@0 | 71 | |
michael@0 | 72 | // Helper used for HTMLCopy and GetTransferableForSelection since both routines |
michael@0 | 73 | // share common code. |
michael@0 | 74 | static nsresult |
michael@0 | 75 | SelectionCopyHelper(nsISelection *aSel, nsIDocument *aDoc, |
michael@0 | 76 | bool doPutOnClipboard, int16_t aClipboardID, |
michael@0 | 77 | uint32_t aFlags, nsITransferable ** aTransferable) |
michael@0 | 78 | { |
michael@0 | 79 | // Clear the output parameter for the transferable, if provided. |
michael@0 | 80 | if (aTransferable) { |
michael@0 | 81 | *aTransferable = nullptr; |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | nsresult rv; |
michael@0 | 85 | |
michael@0 | 86 | nsCOMPtr<nsIDocumentEncoder> docEncoder; |
michael@0 | 87 | docEncoder = do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID); |
michael@0 | 88 | NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE); |
michael@0 | 89 | |
michael@0 | 90 | // note that we assign text/unicode as mime type, but in fact nsHTMLCopyEncoder |
michael@0 | 91 | // ignore it and use text/html or text/plain depending where the selection |
michael@0 | 92 | // is. if it is a selection into input/textarea element or in a html content |
michael@0 | 93 | // with pre-wrap style : text/plain. Otherwise text/html. |
michael@0 | 94 | // see nsHTMLCopyEncoder::SetSelection |
michael@0 | 95 | nsAutoString mimeType; |
michael@0 | 96 | mimeType.AssignLiteral(kUnicodeMime); |
michael@0 | 97 | |
michael@0 | 98 | // Do the first and potentially trial encoding as preformatted and raw. |
michael@0 | 99 | uint32_t flags = aFlags | nsIDocumentEncoder::OutputPreformatted |
michael@0 | 100 | | nsIDocumentEncoder::OutputRaw; |
michael@0 | 101 | |
michael@0 | 102 | nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc); |
michael@0 | 103 | NS_ASSERTION(domDoc, "Need a document"); |
michael@0 | 104 | |
michael@0 | 105 | rv = docEncoder->Init(domDoc, mimeType, flags); |
michael@0 | 106 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 107 | |
michael@0 | 108 | rv = docEncoder->SetSelection(aSel); |
michael@0 | 109 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 110 | |
michael@0 | 111 | // SetSelection set the mime type to text/plain if the selection is inside a |
michael@0 | 112 | // text widget. |
michael@0 | 113 | rv = docEncoder->GetMimeType(mimeType); |
michael@0 | 114 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 115 | bool selForcedTextPlain = mimeType.EqualsLiteral(kTextMime); |
michael@0 | 116 | |
michael@0 | 117 | nsAutoString buf; |
michael@0 | 118 | rv = docEncoder->EncodeToString(buf); |
michael@0 | 119 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 120 | |
michael@0 | 121 | rv = docEncoder->GetMimeType(mimeType); |
michael@0 | 122 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 123 | |
michael@0 | 124 | if (!selForcedTextPlain && mimeType.EqualsLiteral(kTextMime)) { |
michael@0 | 125 | // SetSelection and EncodeToString use this case to signal that text/plain |
michael@0 | 126 | // was forced because the document is either not an nsIHTMLDocument or it's |
michael@0 | 127 | // XHTML. We want to pretty print XHTML but not non-nsIHTMLDocuments. |
michael@0 | 128 | nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aDoc); |
michael@0 | 129 | if (!htmlDoc) { |
michael@0 | 130 | selForcedTextPlain = true; |
michael@0 | 131 | } |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | // The mime type is ultimately text/html if the encoder successfully encoded |
michael@0 | 135 | // the selection as text/html. |
michael@0 | 136 | bool encodedTextHTML = mimeType.EqualsLiteral(kHTMLMime); |
michael@0 | 137 | |
michael@0 | 138 | // First, prepare the text/plain clipboard flavor. |
michael@0 | 139 | nsAutoString textPlainBuf; |
michael@0 | 140 | if (selForcedTextPlain) { |
michael@0 | 141 | // Nothing to do. buf contains the final, preformatted, raw text/plain. |
michael@0 | 142 | textPlainBuf.Assign(buf); |
michael@0 | 143 | } else { |
michael@0 | 144 | // Redo the encoding, but this time use pretty printing. |
michael@0 | 145 | flags = |
michael@0 | 146 | nsIDocumentEncoder::OutputSelectionOnly | |
michael@0 | 147 | nsIDocumentEncoder::OutputAbsoluteLinks | |
michael@0 | 148 | nsIDocumentEncoder::SkipInvisibleContent | |
michael@0 | 149 | nsIDocumentEncoder::OutputDropInvisibleBreak | |
michael@0 | 150 | (aFlags & nsIDocumentEncoder::OutputNoScriptContent); |
michael@0 | 151 | |
michael@0 | 152 | mimeType.AssignLiteral(kTextMime); |
michael@0 | 153 | rv = docEncoder->Init(domDoc, mimeType, flags); |
michael@0 | 154 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 155 | |
michael@0 | 156 | rv = docEncoder->SetSelection(aSel); |
michael@0 | 157 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 158 | |
michael@0 | 159 | rv = docEncoder->EncodeToString(textPlainBuf); |
michael@0 | 160 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | // Second, prepare the text/html flavor. |
michael@0 | 164 | nsAutoString textHTMLBuf; |
michael@0 | 165 | nsAutoString htmlParentsBuf; |
michael@0 | 166 | nsAutoString htmlInfoBuf; |
michael@0 | 167 | if (encodedTextHTML) { |
michael@0 | 168 | // Redo the encoding, but this time use the passed-in flags. |
michael@0 | 169 | mimeType.AssignLiteral(kHTMLMime); |
michael@0 | 170 | rv = docEncoder->Init(domDoc, mimeType, aFlags); |
michael@0 | 171 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 172 | |
michael@0 | 173 | rv = docEncoder->SetSelection(aSel); |
michael@0 | 174 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 175 | |
michael@0 | 176 | rv = docEncoder->EncodeToStringWithContext(htmlParentsBuf, htmlInfoBuf, |
michael@0 | 177 | textHTMLBuf); |
michael@0 | 178 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | // Get the Clipboard |
michael@0 | 182 | nsCOMPtr<nsIClipboard> clipboard; |
michael@0 | 183 | if (doPutOnClipboard) { |
michael@0 | 184 | clipboard = do_GetService(kCClipboardCID, &rv); |
michael@0 | 185 | if (NS_FAILED(rv)) |
michael@0 | 186 | return rv; |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | if ((doPutOnClipboard && clipboard) || aTransferable != nullptr) { |
michael@0 | 190 | // Create a transferable for putting data on the Clipboard |
michael@0 | 191 | nsCOMPtr<nsITransferable> trans = do_CreateInstance(kCTransferableCID); |
michael@0 | 192 | if (trans) { |
michael@0 | 193 | trans->Init(aDoc->GetLoadContext()); |
michael@0 | 194 | if (encodedTextHTML) { |
michael@0 | 195 | // Set up a format converter so that clipboard flavor queries work. |
michael@0 | 196 | // This converter isn't really used for conversions. |
michael@0 | 197 | nsCOMPtr<nsIFormatConverter> htmlConverter = |
michael@0 | 198 | do_CreateInstance(kHTMLConverterCID); |
michael@0 | 199 | trans->SetConverter(htmlConverter); |
michael@0 | 200 | |
michael@0 | 201 | if (!textHTMLBuf.IsEmpty()) { |
michael@0 | 202 | // Add the html DataFlavor to the transferable |
michael@0 | 203 | rv = AppendString(trans, textHTMLBuf, kHTMLMime); |
michael@0 | 204 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | // Add the htmlcontext DataFlavor to the transferable |
michael@0 | 208 | // Even if parents is empty string, this flavor should |
michael@0 | 209 | // be attached to the transferable |
michael@0 | 210 | rv = AppendString(trans, htmlParentsBuf, kHTMLContext); |
michael@0 | 211 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 212 | |
michael@0 | 213 | if (!htmlInfoBuf.IsEmpty()) { |
michael@0 | 214 | // Add the htmlinfo DataFlavor to the transferable |
michael@0 | 215 | rv = AppendString(trans, htmlInfoBuf, kHTMLInfo); |
michael@0 | 216 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | if (!textPlainBuf.IsEmpty()) { |
michael@0 | 220 | // unicode text |
michael@0 | 221 | // Add the unicode DataFlavor to the transferable |
michael@0 | 222 | // If we didn't have this, then nsDataObj::GetData matches text/unicode against |
michael@0 | 223 | // the kURLMime flavour which is not desirable (eg. when pasting into Notepad) |
michael@0 | 224 | rv = AppendString(trans, textPlainBuf, kUnicodeMime); |
michael@0 | 225 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | // Try and get source URI of the items that are being dragged |
michael@0 | 229 | nsIURI *uri = aDoc->GetDocumentURI(); |
michael@0 | 230 | if (uri) { |
michael@0 | 231 | nsAutoCString spec; |
michael@0 | 232 | uri->GetSpec(spec); |
michael@0 | 233 | if (!spec.IsEmpty()) { |
michael@0 | 234 | nsAutoString shortcut; |
michael@0 | 235 | AppendUTF8toUTF16(spec, shortcut); |
michael@0 | 236 | |
michael@0 | 237 | // Add the URL DataFlavor to the transferable. Don't use kURLMime, as it will |
michael@0 | 238 | // cause an unnecessary UniformResourceLocator to be added which confuses |
michael@0 | 239 | // some apps eg. Outlook 2000 - (See Bug 315370). Don't use |
michael@0 | 240 | // kURLDataMime, as it will cause a bogus 'url ' flavor to |
michael@0 | 241 | // show up on the Mac clipboard, confusing other apps, like |
michael@0 | 242 | // Terminal (see bug 336012). |
michael@0 | 243 | rv = AppendString(trans, shortcut, kURLPrivateMime); |
michael@0 | 244 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 245 | } |
michael@0 | 246 | } |
michael@0 | 247 | } else { |
michael@0 | 248 | if (!textPlainBuf.IsEmpty()) { |
michael@0 | 249 | // Add the unicode DataFlavor to the transferable |
michael@0 | 250 | rv = AppendString(trans, textPlainBuf, kUnicodeMime); |
michael@0 | 251 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 252 | } |
michael@0 | 253 | } |
michael@0 | 254 | |
michael@0 | 255 | if (doPutOnClipboard && clipboard) { |
michael@0 | 256 | bool actuallyPutOnClipboard = true; |
michael@0 | 257 | nsCopySupport::DoHooks(aDoc, trans, &actuallyPutOnClipboard); |
michael@0 | 258 | |
michael@0 | 259 | // put the transferable on the clipboard |
michael@0 | 260 | if (actuallyPutOnClipboard) |
michael@0 | 261 | clipboard->SetData(trans, nullptr, aClipboardID); |
michael@0 | 262 | } |
michael@0 | 263 | |
michael@0 | 264 | // Return the transferable to the caller if requested. |
michael@0 | 265 | if (aTransferable != nullptr) { |
michael@0 | 266 | trans.swap(*aTransferable); |
michael@0 | 267 | } |
michael@0 | 268 | } |
michael@0 | 269 | } |
michael@0 | 270 | return rv; |
michael@0 | 271 | } |
michael@0 | 272 | |
michael@0 | 273 | nsresult |
michael@0 | 274 | nsCopySupport::HTMLCopy(nsISelection* aSel, nsIDocument* aDoc, |
michael@0 | 275 | int16_t aClipboardID) |
michael@0 | 276 | { |
michael@0 | 277 | return SelectionCopyHelper(aSel, aDoc, true, aClipboardID, |
michael@0 | 278 | nsIDocumentEncoder::SkipInvisibleContent, |
michael@0 | 279 | nullptr); |
michael@0 | 280 | } |
michael@0 | 281 | |
michael@0 | 282 | nsresult |
michael@0 | 283 | nsCopySupport::GetTransferableForSelection(nsISelection* aSel, |
michael@0 | 284 | nsIDocument* aDoc, |
michael@0 | 285 | nsITransferable** aTransferable) |
michael@0 | 286 | { |
michael@0 | 287 | return SelectionCopyHelper(aSel, aDoc, false, 0, |
michael@0 | 288 | nsIDocumentEncoder::SkipInvisibleContent, |
michael@0 | 289 | aTransferable); |
michael@0 | 290 | } |
michael@0 | 291 | |
michael@0 | 292 | nsresult |
michael@0 | 293 | nsCopySupport::GetTransferableForNode(nsINode* aNode, |
michael@0 | 294 | nsIDocument* aDoc, |
michael@0 | 295 | nsITransferable** aTransferable) |
michael@0 | 296 | { |
michael@0 | 297 | nsCOMPtr<nsISelection> selection; |
michael@0 | 298 | // Make a temporary selection with aNode in a single range. |
michael@0 | 299 | nsresult rv = NS_NewDomSelection(getter_AddRefs(selection)); |
michael@0 | 300 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 301 | nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode); |
michael@0 | 302 | NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); |
michael@0 | 303 | nsRefPtr<nsRange> range = new nsRange(aNode); |
michael@0 | 304 | rv = range->SelectNode(node); |
michael@0 | 305 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 306 | rv = selection->AddRange(range); |
michael@0 | 307 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 308 | // It's not the primary selection - so don't skip invisible content. |
michael@0 | 309 | uint32_t flags = 0; |
michael@0 | 310 | return SelectionCopyHelper(selection, aDoc, false, 0, flags, |
michael@0 | 311 | aTransferable); |
michael@0 | 312 | } |
michael@0 | 313 | |
michael@0 | 314 | nsresult nsCopySupport::DoHooks(nsIDocument *aDoc, nsITransferable *aTrans, |
michael@0 | 315 | bool *aDoPutOnClipboard) |
michael@0 | 316 | { |
michael@0 | 317 | NS_ENSURE_ARG(aDoc); |
michael@0 | 318 | |
michael@0 | 319 | *aDoPutOnClipboard = true; |
michael@0 | 320 | |
michael@0 | 321 | nsCOMPtr<nsISupports> container = aDoc->GetContainer(); |
michael@0 | 322 | nsCOMPtr<nsIClipboardDragDropHookList> hookObj = do_GetInterface(container); |
michael@0 | 323 | if (!hookObj) return NS_ERROR_FAILURE; |
michael@0 | 324 | |
michael@0 | 325 | nsCOMPtr<nsISimpleEnumerator> enumerator; |
michael@0 | 326 | hookObj->GetHookEnumerator(getter_AddRefs(enumerator)); |
michael@0 | 327 | if (!enumerator) return NS_ERROR_FAILURE; |
michael@0 | 328 | |
michael@0 | 329 | // the logic here should follow the behavior specified in |
michael@0 | 330 | // nsIClipboardDragDropHooks.h |
michael@0 | 331 | |
michael@0 | 332 | nsCOMPtr<nsIClipboardDragDropHooks> override; |
michael@0 | 333 | nsCOMPtr<nsISupports> isupp; |
michael@0 | 334 | bool hasMoreHooks = false; |
michael@0 | 335 | nsresult rv = NS_OK; |
michael@0 | 336 | while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) |
michael@0 | 337 | && hasMoreHooks) |
michael@0 | 338 | { |
michael@0 | 339 | rv = enumerator->GetNext(getter_AddRefs(isupp)); |
michael@0 | 340 | if (NS_FAILED(rv)) break; |
michael@0 | 341 | override = do_QueryInterface(isupp); |
michael@0 | 342 | if (override) |
michael@0 | 343 | { |
michael@0 | 344 | #ifdef DEBUG |
michael@0 | 345 | nsresult hookResult = |
michael@0 | 346 | #endif |
michael@0 | 347 | override->OnCopyOrDrag(nullptr, aTrans, aDoPutOnClipboard); |
michael@0 | 348 | NS_ASSERTION(NS_SUCCEEDED(hookResult), "OnCopyOrDrag hook failed"); |
michael@0 | 349 | if (!*aDoPutOnClipboard) |
michael@0 | 350 | break; |
michael@0 | 351 | } |
michael@0 | 352 | } |
michael@0 | 353 | |
michael@0 | 354 | return rv; |
michael@0 | 355 | } |
michael@0 | 356 | |
michael@0 | 357 | nsresult |
michael@0 | 358 | nsCopySupport::GetContents(const nsACString& aMimeType, uint32_t aFlags, nsISelection *aSel, nsIDocument *aDoc, nsAString& outdata) |
michael@0 | 359 | { |
michael@0 | 360 | nsresult rv = NS_OK; |
michael@0 | 361 | |
michael@0 | 362 | nsCOMPtr<nsIDocumentEncoder> docEncoder; |
michael@0 | 363 | |
michael@0 | 364 | nsAutoCString encoderContractID(NS_DOC_ENCODER_CONTRACTID_BASE); |
michael@0 | 365 | encoderContractID.Append(aMimeType); |
michael@0 | 366 | |
michael@0 | 367 | docEncoder = do_CreateInstance(encoderContractID.get()); |
michael@0 | 368 | NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE); |
michael@0 | 369 | |
michael@0 | 370 | uint32_t flags = aFlags | nsIDocumentEncoder::SkipInvisibleContent; |
michael@0 | 371 | |
michael@0 | 372 | if (aMimeType.Equals("text/plain")) |
michael@0 | 373 | flags |= nsIDocumentEncoder::OutputPreformatted; |
michael@0 | 374 | |
michael@0 | 375 | NS_ConvertASCIItoUTF16 unicodeMimeType(aMimeType); |
michael@0 | 376 | |
michael@0 | 377 | nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc); |
michael@0 | 378 | NS_ASSERTION(domDoc, "Need a document"); |
michael@0 | 379 | |
michael@0 | 380 | rv = docEncoder->Init(domDoc, unicodeMimeType, flags); |
michael@0 | 381 | if (NS_FAILED(rv)) return rv; |
michael@0 | 382 | |
michael@0 | 383 | if (aSel) |
michael@0 | 384 | { |
michael@0 | 385 | rv = docEncoder->SetSelection(aSel); |
michael@0 | 386 | if (NS_FAILED(rv)) return rv; |
michael@0 | 387 | } |
michael@0 | 388 | |
michael@0 | 389 | // encode the selection |
michael@0 | 390 | return docEncoder->EncodeToString(outdata); |
michael@0 | 391 | } |
michael@0 | 392 | |
michael@0 | 393 | |
michael@0 | 394 | nsresult |
michael@0 | 395 | nsCopySupport::ImageCopy(nsIImageLoadingContent* aImageElement, |
michael@0 | 396 | nsILoadContext* aLoadContext, |
michael@0 | 397 | int32_t aCopyFlags) |
michael@0 | 398 | { |
michael@0 | 399 | nsresult rv; |
michael@0 | 400 | |
michael@0 | 401 | // create a transferable for putting data on the Clipboard |
michael@0 | 402 | nsCOMPtr<nsITransferable> trans(do_CreateInstance(kCTransferableCID, &rv)); |
michael@0 | 403 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 404 | trans->Init(aLoadContext); |
michael@0 | 405 | |
michael@0 | 406 | if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_TEXT) { |
michael@0 | 407 | // get the location from the element |
michael@0 | 408 | nsCOMPtr<nsIURI> uri; |
michael@0 | 409 | rv = aImageElement->GetCurrentURI(getter_AddRefs(uri)); |
michael@0 | 410 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 411 | NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); |
michael@0 | 412 | |
michael@0 | 413 | nsAutoCString location; |
michael@0 | 414 | rv = uri->GetSpec(location); |
michael@0 | 415 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 416 | |
michael@0 | 417 | // append the string to the transferable |
michael@0 | 418 | rv = AppendString(trans, NS_ConvertUTF8toUTF16(location), kUnicodeMime); |
michael@0 | 419 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 420 | } |
michael@0 | 421 | |
michael@0 | 422 | if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_HTML) { |
michael@0 | 423 | // append HTML data to the transferable |
michael@0 | 424 | nsCOMPtr<nsINode> node(do_QueryInterface(aImageElement, &rv)); |
michael@0 | 425 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 426 | |
michael@0 | 427 | rv = AppendDOMNode(trans, node); |
michael@0 | 428 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 429 | } |
michael@0 | 430 | |
michael@0 | 431 | if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_DATA) { |
michael@0 | 432 | // get the image data from the element |
michael@0 | 433 | nsCOMPtr<imgIContainer> image = |
michael@0 | 434 | nsContentUtils::GetImageFromContent(aImageElement); |
michael@0 | 435 | NS_ENSURE_TRUE(image, NS_ERROR_FAILURE); |
michael@0 | 436 | |
michael@0 | 437 | nsCOMPtr<nsISupportsInterfacePointer> |
michael@0 | 438 | imgPtr(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv)); |
michael@0 | 439 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 440 | |
michael@0 | 441 | rv = imgPtr->SetData(image); |
michael@0 | 442 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 443 | |
michael@0 | 444 | // copy the image data onto the transferable |
michael@0 | 445 | rv = trans->SetTransferData(kNativeImageMime, imgPtr, |
michael@0 | 446 | sizeof(nsISupports*)); |
michael@0 | 447 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 448 | } |
michael@0 | 449 | |
michael@0 | 450 | // get clipboard |
michael@0 | 451 | nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); |
michael@0 | 452 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 453 | |
michael@0 | 454 | // check whether the system supports the selection clipboard or not. |
michael@0 | 455 | bool selectionSupported; |
michael@0 | 456 | rv = clipboard->SupportsSelectionClipboard(&selectionSupported); |
michael@0 | 457 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 458 | |
michael@0 | 459 | // put the transferable on the clipboard |
michael@0 | 460 | if (selectionSupported) { |
michael@0 | 461 | rv = clipboard->SetData(trans, nullptr, nsIClipboard::kSelectionClipboard); |
michael@0 | 462 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 463 | } |
michael@0 | 464 | |
michael@0 | 465 | return clipboard->SetData(trans, nullptr, nsIClipboard::kGlobalClipboard); |
michael@0 | 466 | } |
michael@0 | 467 | |
michael@0 | 468 | static nsresult AppendString(nsITransferable *aTransferable, |
michael@0 | 469 | const nsAString& aString, |
michael@0 | 470 | const char* aFlavor) |
michael@0 | 471 | { |
michael@0 | 472 | nsresult rv; |
michael@0 | 473 | |
michael@0 | 474 | nsCOMPtr<nsISupportsString> |
michael@0 | 475 | data(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv)); |
michael@0 | 476 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 477 | |
michael@0 | 478 | rv = data->SetData(aString); |
michael@0 | 479 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 480 | |
michael@0 | 481 | rv = aTransferable->AddDataFlavor(aFlavor); |
michael@0 | 482 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 483 | |
michael@0 | 484 | return aTransferable->SetTransferData(aFlavor, data, |
michael@0 | 485 | aString.Length() * sizeof(char16_t)); |
michael@0 | 486 | } |
michael@0 | 487 | |
michael@0 | 488 | static nsresult AppendDOMNode(nsITransferable *aTransferable, |
michael@0 | 489 | nsINode *aDOMNode) |
michael@0 | 490 | { |
michael@0 | 491 | nsresult rv; |
michael@0 | 492 | |
michael@0 | 493 | // selializer |
michael@0 | 494 | nsCOMPtr<nsIDocumentEncoder> |
michael@0 | 495 | docEncoder(do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID, &rv)); |
michael@0 | 496 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 497 | |
michael@0 | 498 | // get document for the encoder |
michael@0 | 499 | nsCOMPtr<nsIDocument> document = aDOMNode->OwnerDoc(); |
michael@0 | 500 | |
michael@0 | 501 | // Note that XHTML is not counted as HTML here, because we can't copy it |
michael@0 | 502 | // properly (all the copy code for non-plaintext assumes using HTML |
michael@0 | 503 | // serializers and parsers is OK, and those mess up XHTML). |
michael@0 | 504 | nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document, &rv); |
michael@0 | 505 | NS_ENSURE_SUCCESS(rv, NS_OK); |
michael@0 | 506 | |
michael@0 | 507 | NS_ENSURE_TRUE(document->IsHTML(), NS_OK); |
michael@0 | 508 | |
michael@0 | 509 | // init encoder with document and node |
michael@0 | 510 | rv = docEncoder->NativeInit(document, NS_LITERAL_STRING(kHTMLMime), |
michael@0 | 511 | nsIDocumentEncoder::OutputAbsoluteLinks | |
michael@0 | 512 | nsIDocumentEncoder::OutputEncodeW3CEntities); |
michael@0 | 513 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 514 | |
michael@0 | 515 | rv = docEncoder->SetNativeNode(aDOMNode); |
michael@0 | 516 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 517 | |
michael@0 | 518 | // serialize to string |
michael@0 | 519 | nsAutoString html, context, info; |
michael@0 | 520 | rv = docEncoder->EncodeToStringWithContext(context, info, html); |
michael@0 | 521 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 522 | |
michael@0 | 523 | // copy them to the transferable |
michael@0 | 524 | if (!html.IsEmpty()) { |
michael@0 | 525 | rv = AppendString(aTransferable, html, kHTMLMime); |
michael@0 | 526 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 527 | } |
michael@0 | 528 | |
michael@0 | 529 | if (!info.IsEmpty()) { |
michael@0 | 530 | rv = AppendString(aTransferable, info, kHTMLInfo); |
michael@0 | 531 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 532 | } |
michael@0 | 533 | |
michael@0 | 534 | // add a special flavor, even if we don't have html context data |
michael@0 | 535 | return AppendString(aTransferable, context, kHTMLContext); |
michael@0 | 536 | } |
michael@0 | 537 | |
michael@0 | 538 | nsIContent* |
michael@0 | 539 | nsCopySupport::GetSelectionForCopy(nsIDocument* aDocument, nsISelection** aSelection) |
michael@0 | 540 | { |
michael@0 | 541 | *aSelection = nullptr; |
michael@0 | 542 | |
michael@0 | 543 | nsIPresShell* presShell = aDocument->GetShell(); |
michael@0 | 544 | if (!presShell) |
michael@0 | 545 | return nullptr; |
michael@0 | 546 | |
michael@0 | 547 | // check if the focused node in the window has a selection |
michael@0 | 548 | nsCOMPtr<nsPIDOMWindow> focusedWindow; |
michael@0 | 549 | nsIContent* content = |
michael@0 | 550 | nsFocusManager::GetFocusedDescendant(aDocument->GetWindow(), false, |
michael@0 | 551 | getter_AddRefs(focusedWindow)); |
michael@0 | 552 | if (content) { |
michael@0 | 553 | nsIFrame* frame = content->GetPrimaryFrame(); |
michael@0 | 554 | if (frame) { |
michael@0 | 555 | nsCOMPtr<nsISelectionController> selCon; |
michael@0 | 556 | frame->GetSelectionController(presShell->GetPresContext(), getter_AddRefs(selCon)); |
michael@0 | 557 | if (selCon) { |
michael@0 | 558 | selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection); |
michael@0 | 559 | return content; |
michael@0 | 560 | } |
michael@0 | 561 | } |
michael@0 | 562 | } |
michael@0 | 563 | |
michael@0 | 564 | // if no selection was found, use the main selection for the window |
michael@0 | 565 | NS_IF_ADDREF(*aSelection = presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL)); |
michael@0 | 566 | return nullptr; |
michael@0 | 567 | } |
michael@0 | 568 | |
michael@0 | 569 | bool |
michael@0 | 570 | nsCopySupport::CanCopy(nsIDocument* aDocument) |
michael@0 | 571 | { |
michael@0 | 572 | if (!aDocument) |
michael@0 | 573 | return false; |
michael@0 | 574 | |
michael@0 | 575 | nsCOMPtr<nsISelection> sel; |
michael@0 | 576 | GetSelectionForCopy(aDocument, getter_AddRefs(sel)); |
michael@0 | 577 | NS_ENSURE_TRUE(sel, false); |
michael@0 | 578 | |
michael@0 | 579 | bool isCollapsed; |
michael@0 | 580 | sel->GetIsCollapsed(&isCollapsed); |
michael@0 | 581 | return !isCollapsed; |
michael@0 | 582 | } |
michael@0 | 583 | |
michael@0 | 584 | bool |
michael@0 | 585 | nsCopySupport::FireClipboardEvent(int32_t aType, int32_t aClipboardType, nsIPresShell* aPresShell, nsISelection* aSelection) |
michael@0 | 586 | { |
michael@0 | 587 | NS_ASSERTION(aType == NS_CUT || aType == NS_COPY || aType == NS_PASTE, |
michael@0 | 588 | "Invalid clipboard event type"); |
michael@0 | 589 | |
michael@0 | 590 | nsCOMPtr<nsIPresShell> presShell = aPresShell; |
michael@0 | 591 | if (!presShell) |
michael@0 | 592 | return false; |
michael@0 | 593 | |
michael@0 | 594 | nsCOMPtr<nsIDocument> doc = presShell->GetDocument(); |
michael@0 | 595 | if (!doc) |
michael@0 | 596 | return false; |
michael@0 | 597 | |
michael@0 | 598 | nsCOMPtr<nsPIDOMWindow> piWindow = doc->GetWindow(); |
michael@0 | 599 | if (!piWindow) |
michael@0 | 600 | return false; |
michael@0 | 601 | |
michael@0 | 602 | // if a selection was not supplied, try to find it |
michael@0 | 603 | nsCOMPtr<nsIContent> content; |
michael@0 | 604 | nsCOMPtr<nsISelection> sel = aSelection; |
michael@0 | 605 | if (!sel) |
michael@0 | 606 | content = GetSelectionForCopy(doc, getter_AddRefs(sel)); |
michael@0 | 607 | |
michael@0 | 608 | // retrieve the event target node from the start of the selection |
michael@0 | 609 | nsresult rv; |
michael@0 | 610 | if (sel) { |
michael@0 | 611 | // Only cut or copy when there is an uncollapsed selection |
michael@0 | 612 | if (aType == NS_CUT || aType == NS_COPY) { |
michael@0 | 613 | bool isCollapsed; |
michael@0 | 614 | sel->GetIsCollapsed(&isCollapsed); |
michael@0 | 615 | if (isCollapsed) |
michael@0 | 616 | return false; |
michael@0 | 617 | } |
michael@0 | 618 | |
michael@0 | 619 | nsCOMPtr<nsIDOMRange> range; |
michael@0 | 620 | rv = sel->GetRangeAt(0, getter_AddRefs(range)); |
michael@0 | 621 | if (NS_SUCCEEDED(rv) && range) { |
michael@0 | 622 | nsCOMPtr<nsIDOMNode> startContainer; |
michael@0 | 623 | range->GetStartContainer(getter_AddRefs(startContainer)); |
michael@0 | 624 | if (startContainer) |
michael@0 | 625 | content = do_QueryInterface(startContainer); |
michael@0 | 626 | } |
michael@0 | 627 | } |
michael@0 | 628 | |
michael@0 | 629 | // if no content node was set, just get the root |
michael@0 | 630 | if (!content) { |
michael@0 | 631 | content = doc->GetRootElement(); |
michael@0 | 632 | if (!content) |
michael@0 | 633 | return false; |
michael@0 | 634 | } |
michael@0 | 635 | |
michael@0 | 636 | // It seems to be unsafe to fire an event handler during reflow (bug 393696) |
michael@0 | 637 | if (!nsContentUtils::IsSafeToRunScript()) |
michael@0 | 638 | return false; |
michael@0 | 639 | |
michael@0 | 640 | nsCOMPtr<nsIDocShell> docShell = do_GetInterface(piWindow); |
michael@0 | 641 | const bool chromeShell = |
michael@0 | 642 | docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome; |
michael@0 | 643 | |
michael@0 | 644 | // next, fire the cut, copy or paste event |
michael@0 | 645 | bool doDefault = true; |
michael@0 | 646 | nsRefPtr<DataTransfer> clipboardData; |
michael@0 | 647 | if (chromeShell || Preferences::GetBool("dom.event.clipboardevents.enabled", true)) { |
michael@0 | 648 | clipboardData = |
michael@0 | 649 | new DataTransfer(piWindow, aType, aType == NS_PASTE, aClipboardType); |
michael@0 | 650 | |
michael@0 | 651 | nsEventStatus status = nsEventStatus_eIgnore; |
michael@0 | 652 | InternalClipboardEvent evt(true, aType); |
michael@0 | 653 | evt.clipboardData = clipboardData; |
michael@0 | 654 | EventDispatcher::Dispatch(content, presShell->GetPresContext(), &evt, |
michael@0 | 655 | nullptr, &status); |
michael@0 | 656 | // If the event was cancelled, don't do the clipboard operation |
michael@0 | 657 | doDefault = (status != nsEventStatus_eConsumeNoDefault); |
michael@0 | 658 | } |
michael@0 | 659 | |
michael@0 | 660 | // No need to do anything special during a paste. Either an event listener |
michael@0 | 661 | // took care of it and cancelled the event, or the caller will handle it. |
michael@0 | 662 | // Return true to indicate that the event wasn't cancelled. |
michael@0 | 663 | if (aType == NS_PASTE) { |
michael@0 | 664 | // Clear and mark the clipboardData as readonly. This prevents someone |
michael@0 | 665 | // from reading the clipboard contents after the paste event has fired. |
michael@0 | 666 | if (clipboardData) { |
michael@0 | 667 | clipboardData->ClearAll(); |
michael@0 | 668 | clipboardData->SetReadOnly(); |
michael@0 | 669 | } |
michael@0 | 670 | |
michael@0 | 671 | return doDefault; |
michael@0 | 672 | } |
michael@0 | 673 | |
michael@0 | 674 | // Update the presentation in case the event handler modified the selection, |
michael@0 | 675 | // see bug 602231. |
michael@0 | 676 | presShell->FlushPendingNotifications(Flush_Frames); |
michael@0 | 677 | if (presShell->IsDestroying()) |
michael@0 | 678 | return false; |
michael@0 | 679 | |
michael@0 | 680 | // if the event was not cancelled, do the default copy. If the event was cancelled, |
michael@0 | 681 | // use the data added to the data transfer and copy that instead. |
michael@0 | 682 | uint32_t count = 0; |
michael@0 | 683 | if (doDefault) { |
michael@0 | 684 | // get the data from the selection if any |
michael@0 | 685 | bool isCollapsed; |
michael@0 | 686 | sel->GetIsCollapsed(&isCollapsed); |
michael@0 | 687 | if (isCollapsed) { |
michael@0 | 688 | return false; |
michael@0 | 689 | } |
michael@0 | 690 | // call the copy code |
michael@0 | 691 | rv = HTMLCopy(sel, doc, aClipboardType); |
michael@0 | 692 | if (NS_FAILED(rv)) { |
michael@0 | 693 | return false; |
michael@0 | 694 | } |
michael@0 | 695 | } else if (clipboardData) { |
michael@0 | 696 | // check to see if any data was put on the data transfer. |
michael@0 | 697 | clipboardData->GetMozItemCount(&count); |
michael@0 | 698 | if (count) { |
michael@0 | 699 | nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1")); |
michael@0 | 700 | NS_ENSURE_TRUE(clipboard, false); |
michael@0 | 701 | |
michael@0 | 702 | nsCOMPtr<nsITransferable> transferable = |
michael@0 | 703 | clipboardData->GetTransferable(0, doc->GetLoadContext()); |
michael@0 | 704 | |
michael@0 | 705 | NS_ENSURE_TRUE(transferable, false); |
michael@0 | 706 | |
michael@0 | 707 | // put the transferable on the clipboard |
michael@0 | 708 | rv = clipboard->SetData(transferable, nullptr, aClipboardType); |
michael@0 | 709 | if (NS_FAILED(rv)) { |
michael@0 | 710 | return false; |
michael@0 | 711 | } |
michael@0 | 712 | } |
michael@0 | 713 | } |
michael@0 | 714 | |
michael@0 | 715 | // Now that we have copied, update the clipboard commands. This should have |
michael@0 | 716 | // the effect of updating the enabled state of the paste menu item. |
michael@0 | 717 | if (doDefault || count) { |
michael@0 | 718 | piWindow->UpdateCommands(NS_LITERAL_STRING("clipboard")); |
michael@0 | 719 | } |
michael@0 | 720 | |
michael@0 | 721 | return doDefault; |
michael@0 | 722 | } |