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