content/base/src/nsCopySupport.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

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 }

mercurial