1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1999-2000
21 * the Initial Developer. All Rights Reserved.
24 * Stuart Parmenter <pavlov@netscape.com>
25 * Mike Pinkerton <pinkerton@netscape.com>
26 * Dan Rosen <dr@netscape.com>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #include "nsClipboard.h"
48 #include "nsISupportsArray.h"
49 #include "nsISupportsPrimitives.h"
50 #include "nsReadableUtils.h"
52 #include "nsIComponentManager.h"
53 #include "nsIServiceManager.h"
54 #include "nsWidgetsCID.h"
55 #include "nsXPIDLString.h"
56 #include "nsReadableUtils.h"
57 #include "nsPrimitiveHelpers.h"
59 #include "nsTextFormatter.h"
61 #include "nsIServiceManager.h"
62 #include "nsICharsetConverterManager.h"
68 #include "nsIPlatformCharset.h"
69 #include "nsICharsetConverterManager.h"
71 //#define DEBUG_CLIPBOARD
74 // Define this to enable the obsolete X cut buffer mechanism
75 // In general, a bad idea (see http://www.jwz.org/doc/x-cut-and-paste.html)
76 // but it might have its uses for backwards compatibility.
78 NS_IMPL_ISUPPORTS1(nsClipboard
, nsIClipboard
)
80 #define Ph_CLIPBOARD_TYPE_MOZ_BOOKMARK "BOOK"
81 #define Ph_CLIPBOARD_TYPE_IMAGE "IMAG"
82 #define Ph_CLIPBOARD_TYPE_HTML "HTML"
84 //-------------------------------------------------------------------------
86 // nsClipboard constructor
88 //-------------------------------------------------------------------------
89 nsClipboard::nsClipboard()
91 #ifdef DEBUG_CLIPBOARD
92 printf("nsClipboard::nsClipboard()\n");
93 #endif /* DEBUG_CLIPBOARD */
95 mIgnoreEmptyNotification
= PR_FALSE
;
96 mGlobalTransferable
= nsnull
;
97 mSelectionTransferable
= nsnull
;
98 mGlobalOwner
= nsnull
;
99 mSelectionOwner
= nsnull
;
103 //-------------------------------------------------------------------------
105 // nsClipboard destructor
107 //-------------------------------------------------------------------------
108 nsClipboard::~nsClipboard()
110 #ifdef DEBUG_CLIPBOARD
111 printf("nsClipboard::~nsClipboard()\n");
112 #endif /* DEBUG_CLIPBOARD */
116 * Sets the transferable object
119 NS_IMETHODIMP
nsClipboard::SetData(nsITransferable
* aTransferable
,
120 nsIClipboardOwner
* anOwner
,
121 PRInt32 aWhichClipboard
)
123 if (aWhichClipboard
== kSelectionClipboard
)
124 return (NS_ERROR_FAILURE
);
126 if ((aTransferable
== mGlobalTransferable
.get() && anOwner
== mGlobalOwner
.get() &&
127 aWhichClipboard
== kGlobalClipboard
) || (aTransferable
== mSelectionTransferable
.get() &&
128 anOwner
== mSelectionOwner
.get() && aWhichClipboard
== kSelectionClipboard
))
133 EmptyClipboard(aWhichClipboard
);
135 switch (aWhichClipboard
)
137 case kSelectionClipboard
:
138 mSelectionOwner
= anOwner
;
139 mSelectionTransferable
= aTransferable
;
141 case kGlobalClipboard
:
142 mGlobalOwner
= anOwner
;
143 mGlobalTransferable
= aTransferable
;
147 return SetNativeClipboardData(aWhichClipboard
);
151 * Gets the transferable object
154 NS_IMETHODIMP
nsClipboard::GetData(nsITransferable
* aTransferable
, PRInt32 aWhichClipboard
)
156 if (aWhichClipboard
== kSelectionClipboard
)
157 return (NS_ERROR_FAILURE
);
158 if (nsnull
!= aTransferable
)
159 return GetNativeClipboardData(aTransferable
, aWhichClipboard
);
162 #ifdef DEBUG_CLIPBOARD
163 printf(" nsClipboard::GetData(), aTransferable is NULL.\n");
167 return NS_ERROR_FAILURE
;
175 NS_IMETHODIMP
nsClipboard::EmptyClipboard(PRInt32 aWhichClipboard
)
177 if (mIgnoreEmptyNotification
)
180 if (aWhichClipboard
== kSelectionClipboard
)
181 return (NS_ERROR_FAILURE
);
183 switch(aWhichClipboard
)
185 case kSelectionClipboard
:
186 return NS_ERROR_FAILURE
;
189 mSelectionOwner
->LosingOwnership(mSelectionTransferable
);
190 mSelectionOwner
= nsnull
;
192 mSelectionTransferable
= nsnull
;
194 case kGlobalClipboard
:
197 mGlobalOwner
->LosingOwnership(mGlobalTransferable
);
198 mGlobalOwner
= nsnull
;
200 mGlobalTransferable
= nsnull
;
207 NS_IMETHODIMP
nsClipboard::SupportsSelectionClipboard(PRBool
*_retval
)
209 NS_ENSURE_ARG_POINTER(_retval
);
211 *_retval
= PR_FALSE
; // we support the selection clipboard on unix.
212 return NS_ERROR_FAILURE
;
215 //-------------------------------------------------------------------------
216 NS_IMETHODIMP
nsClipboard::SetNativeClipboardData(PRInt32 aWhichClipboard
)
218 mIgnoreEmptyNotification
= PR_TRUE
;
219 if (aWhichClipboard
== kSelectionClipboard
)
220 return (NS_ERROR_FAILURE
);
222 #ifdef DEBUG_CLIPBOARD
223 printf(" nsClipboard::SetNativeClipboardData(%i)\n", aWhichClipboard
);
224 #endif /* DEBUG_CLIPBOARD */
226 nsCOMPtr
<nsITransferable
> transferable(GetTransferable(aWhichClipboard
));
228 // make sure we have a good transferable
229 if (nsnull
== transferable
)
231 #ifdef DEBUG_CLIPBOARD
232 printf("nsClipboard::SetNativeClipboardData(): no transferable!\n");
234 return NS_ERROR_FAILURE
;
237 // get flavor list that includes all flavors that can be written (including ones
238 // obtained through conversion)
239 nsCOMPtr
<nsISupportsArray
> flavorList
;
240 nsresult errCode
= transferable
->FlavorsTransferableCanExport ( getter_AddRefs(flavorList
) );
241 if ( NS_FAILED(errCode
) )
242 return NS_ERROR_FAILURE
;
244 PRUint32 cnt
, index
= 0;
245 flavorList
->Count(&cnt
);
246 PhClipHeader
*cliphdr
= (PhClipHeader
*) calloc( cnt
, sizeof( PhClipHeader
));
247 if( !cliphdr
) return NS_ERROR_FAILURE
;
249 for ( PRUint32 k
=0; k
<cnt
; ++k
)
254 nsCOMPtr
<nsISupports
> genericFlavor
;
255 flavorList
->GetElementAt ( k
, getter_AddRefs(genericFlavor
) );
256 nsCOMPtr
<nsISupportsCString
> currentFlavor ( do_QueryInterface(genericFlavor
) );
259 nsXPIDLCString flavorStr
;
260 currentFlavor
->ToString(getter_Copies(flavorStr
));
262 nsresult err
= GetFormat( flavorStr
, cliphdr
[index
].type
);
266 // Get data out of transferable.
267 nsCOMPtr
<nsISupports
> genericDataWrapper
;
268 transferable
->GetTransferData( flavorStr
, getter_AddRefs(genericDataWrapper
), &dataLen
);
269 nsPrimitiveHelpers::CreateDataFromPrimitive ( flavorStr
, genericDataWrapper
, &data
, dataLen
);
271 if( !strcmp(cliphdr
[index
].type
, Ph_CLIPBOARD_TYPE_TEXT
) ||
272 !strcmp(cliphdr
[index
].type
, Ph_CLIPBOARD_TYPE_HTML
) ||
273 !strcmp(cliphdr
[index
].type
, Ph_CLIPBOARD_TYPE_MOZ_BOOKMARK
) )
275 PRUnichar
* castedUnicode
= reinterpret_cast<PRUnichar
*>(data
);
276 char *utf8String
= ToNewUTF8String(nsDependentString(castedUnicode
, dataLen
/2));
277 nsMemory::Free(reinterpret_cast<char*>(data
));
279 if( !strcmp(cliphdr
[index
].type
, Ph_CLIPBOARD_TYPE_TEXT
) )
281 /* we have to create a null terminated string, because
282 PhClipboardCopyString does that and some other applications
283 rely on the null terminated thing
285 PRInt32 len
= strlen(utf8String
);
286 char *temp
= ( char * ) nsMemory::Alloc( len
+ 1 );
287 memcpy( temp
, utf8String
, len
);
289 nsMemory::Free(reinterpret_cast<char*>(utf8String
));
291 cliphdr
[index
].length
= len
+1;
292 cliphdr
[index
].data
= temp
;
295 cliphdr
[index
].length
= strlen(utf8String
);
296 cliphdr
[index
].data
= utf8String
;
303 PhClipboardCopy( mInputGroup
, index
, cliphdr
);
304 for( PRUint32 k
=0; k
<index
; k
++)
305 nsMemory::Free(reinterpret_cast<char*>(cliphdr
[k
].data
));
309 mIgnoreEmptyNotification
= PR_FALSE
;
315 //-------------------------------------------------------------------------
317 // The blocking Paste routine
319 //-------------------------------------------------------------------------
321 nsClipboard::GetNativeClipboardData(nsITransferable
* aTransferable
,
322 PRInt32 aWhichClipboard
)
324 if (aWhichClipboard
== kSelectionClipboard
)
325 return (NS_ERROR_FAILURE
);
327 #ifdef DEBUG_CLIPBOARD
328 printf("nsClipboard::GetNativeClipboardData(%i)\n", aWhichClipboard
);
329 #endif /* DEBUG_CLIPBOARD */
331 // make sure we have a good transferable
332 if (nsnull
== aTransferable
)
334 #ifdef DEBUG_CLIPBOARD
335 printf(" GetNativeClipboardData: Transferable is null!\n");
337 return NS_ERROR_FAILURE
;
340 // get flavor list that includes all acceptable flavors (including ones obtained through
342 nsCOMPtr
<nsISupportsArray
> flavorList
;
343 nsresult errCode
= aTransferable
->FlavorsTransferableCanImport ( getter_AddRefs(flavorList
) );
344 if ( NS_FAILED(errCode
) )
345 return NS_ERROR_FAILURE
;
347 // Walk through flavors and see which flavor matches the one being pasted:
349 flavorList
->Count(&cnt
);
350 nsCAutoString foundFlavor
;
355 PhClipHeader
*cliphdr
;
356 char *data
= nsnull
, type
[8];
359 clipPtr
= PhClipboardPasteStart( mInputGroup
);
360 if(!clipPtr
) return NS_ERROR_FAILURE
;
363 Look at the timestamps of the data in the clipboard and eliminate the flavours if they are not synchronized.
364 We can have a HTML flavour from a previous copy and a TEXT flavour from a more recent copy from another application
365 ( from instance from ped or pterm ). The HTML flavour and TEXT flavour are desynchronized and we have
366 to use only the most recent one */
367 unsigned long *dont_use_flavour
= ( unsigned long * ) calloc( cnt
, sizeof( unsigned long ) );
368 if( !dont_use_flavour
) {
369 PhClipboardPasteFinish( clipPtr
);
370 return NS_ERROR_FAILURE
;
373 unsigned long max_time
= 0;
376 for ( i
= 0; i
< cnt
; ++i
)
378 nsCOMPtr
<nsISupports
> genericFlavor
;
379 flavorList
->GetElementAt ( i
, getter_AddRefs(genericFlavor
) );
380 nsCOMPtr
<nsISupportsCString
> currentFlavor ( do_QueryInterface(genericFlavor
) );
383 nsXPIDLCString flavorStr
;
384 currentFlavor
->ToString ( getter_Copies(flavorStr
) );
386 nsresult err
= GetFormat( flavorStr
, type
);
390 dont_use_flavour
[i
] = GetFlavourTimestamp( type
);
391 if( dont_use_flavour
[i
] > max_time
) max_time
= dont_use_flavour
[i
];
395 for ( i
= 0; i
< cnt
; ++i
)
397 if( abs( dont_use_flavour
[i
] - max_time
) >= 4 )
398 dont_use_flavour
[i
] = 1; /* this flavour is desynchronized */
399 else dont_use_flavour
[i
] = 0; /* this flavour is ok */
402 for ( i
= 0; i
< cnt
; ++i
)
404 if( dont_use_flavour
[i
] ) continue; /* this flavour is desynchronized */
405 nsCOMPtr
<nsISupports
> genericFlavor
;
406 flavorList
->GetElementAt ( i
, getter_AddRefs(genericFlavor
) );
407 nsCOMPtr
<nsISupportsCString
> currentFlavor ( do_QueryInterface(genericFlavor
) );
410 nsXPIDLCString flavorStr
;
411 currentFlavor
->ToString ( getter_Copies(flavorStr
) );
413 nsresult err
= GetFormat( flavorStr
, type
);
417 cliphdr
= PhClipboardPasteType( clipPtr
, type
);
420 data
= (char*)cliphdr
->data
;
422 if( !strcmp(type
, Ph_CLIPBOARD_TYPE_TEXT
) )
423 /* for the Ph_CLIPBOARD_TYPE_TEXT, we null terminate the data, since PhClipboardCopyString() does that */
424 dataLen
= cliphdr
->length
- 1;
425 else dataLen
= cliphdr
->length
;
428 if( !strcmp(type
, Ph_CLIPBOARD_TYPE_TEXT
) ||
429 !strcmp(type
, Ph_CLIPBOARD_TYPE_HTML
) ||
430 !strcmp(type
, Ph_CLIPBOARD_TYPE_MOZ_BOOKMARK
) )
433 PRInt32 outUnicodeLen
;
434 PRUnichar
*unicodeData
= nsnull
;
437 nsCOMPtr
<nsIUnicodeDecoder
> decoder
;
438 nsCOMPtr
<nsICharsetConverterManager
> ccm
= do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID
, &rv
);
439 rv
= ccm
->GetUnicodeDecoderRaw("UTF-8", getter_AddRefs(decoder
));
441 if( NS_SUCCEEDED(rv
) )
444 decoder
->GetMaxLength(data
, dataLen
, &outUnicodeLen
); // |outUnicodeLen| is number of chars
446 unicodeData
= reinterpret_cast<PRUnichar
*>(nsMemory::Alloc((outUnicodeLen
+ 1) * sizeof(PRUnichar
)));
448 PRInt32 numberTmp
= dataLen
;
449 rv
= decoder
->Convert(data
, &numberTmp
, unicodeData
, &outUnicodeLen
);
450 #ifdef DEBUG_CLIPBOARD
451 if (numberTmp
!= dataLen
)
452 printf("didn't consume all the bytes\n");
455 (unicodeData
)[outUnicodeLen
] = '\0'; // null terminate. Convert() doesn't do it for us
460 data
= reinterpret_cast<char*>(unicodeData
);
461 dataLen
= outUnicodeLen
* 2;
463 nsCOMPtr
<nsISupports
> genericDataWrapper
;
464 nsPrimitiveHelpers::CreatePrimitiveForData( flavorStr
, data
, dataLen
, getter_AddRefs(genericDataWrapper
) );
465 aTransferable
->SetTransferData( flavorStr
, genericDataWrapper
, dataLen
);
467 /* free the allocated memory */
468 nsMemory::Free( unicodeData
);
477 free( dont_use_flavour
);
478 PhClipboardPasteFinish( clipPtr
);
485 nsClipboard::HasDataMatchingFlavors(const char** aFlavorList
,
487 PRInt32 aWhichClipboard
,
490 if (aWhichClipboard
== kSelectionClipboard
)
491 return (NS_ERROR_FAILURE
);
492 // XXX this doesn't work right. need to fix it.
494 // Note to implementor...(from pink the clipboard bitch).
496 // If a client asks for unicode, first check if unicode is present. If not, then
497 // check for plain text. If it's there, say "yes" as we will do the conversion
498 // in GetNativeClipboardData(). From this point on, no client will
499 // ever ask for text/plain explicitly. If they do, you must ASSERT!
500 #ifdef DEBUG_CLIPBOARD
501 printf(" nsClipboard::HasDataMatchingFlavors()\n {\n");
504 nsresult res
= NS_OK
;
505 * outResult
= PR_FALSE
;
507 // Walk through flavors and see which flavor matches the one being pasted:
508 nsCAutoString foundFlavor
;
512 PhClipHeader
*cliphdr
;
514 clipPtr
= PhClipboardPasteStart( 1 );
515 if(nsnull
== clipPtr
)
518 for ( PRUint32 i
= 0; i
< aLength
; ++i
) {
519 nsresult err
= GetFormat( aFlavorList
[i
], type
);
520 if (err
!= NS_OK
) continue;
522 cliphdr
= PhClipboardPasteType( clipPtr
, type
);
526 *outResult
= PR_TRUE
;
530 PhClipboardPasteFinish( clipPtr
);
537 nsresult
nsClipboard::GetFormat(const char* aMimeStr
, char *format
)
539 nsDependentCString
mimeStr(aMimeStr
);
542 if( mimeStr
.Equals(kUnicodeMime
) || mimeStr
.Equals(kTextMime
) )
543 strcpy( format
, Ph_CLIPBOARD_TYPE_TEXT
);
544 else if( mimeStr
.Equals(kHTMLMime
) )
545 strcpy( format
, Ph_CLIPBOARD_TYPE_HTML
);
546 else if (mimeStr
.Equals("moz/bookmarkclipboarditem"))
547 strcpy( format
, Ph_CLIPBOARD_TYPE_MOZ_BOOKMARK
);
548 else ret
= NS_ERROR_FAILURE
;
553 nsITransferable
*nsClipboard::GetTransferable(PRInt32 aWhichClipboard
)
555 nsITransferable
*transferable
= nsnull
;
556 switch (aWhichClipboard
)
558 case kGlobalClipboard
:
559 transferable
= mGlobalTransferable
;
561 case kSelectionClipboard
:
567 unsigned long nsClipboard::GetFlavourTimestamp( char *type
)
570 extern struct _Ph_ctrl
*_Ph_
;
572 strcpy( fname
, "/var/clipboard/" );
573 if( access( fname
, X_OK
) != 0 )
577 if( fstat( _Ph_
->fd
, &buf
) != 0 )
580 if(gethostname(&fname
[strlen(fname
)],PATH_MAX
-40)!=0)
581 strcpy(&fname
[strlen(fname
)],"localhost");
583 sprintf( &fname
[strlen(fname
)], "/%08x/%d.%s",buf
.st_uid
, mInputGroup
, type
);
585 if( stat( fname
, &st
) != 0 )