LCOV - code coverage report
Current view: directory - widget/gtk2 - nsClipboard.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 416 0 0.0 %
Date: 2012-06-02 Functions: 32 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : /* vim:expandtab:shiftwidth=4:tabstop=4:
       3                 :  */
       4                 : /* ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is mozilla.org code.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is Christopher Blizzard
      20                 :  * <blizzard@mozilla.org>.  Portions created by the Initial Developer
      21                 :  * are Copyright (C) 2001 the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "mozilla/Util.h"
      40                 : 
      41                 : #include "nsClipboard.h"
      42                 : #include "nsSupportsPrimitives.h"
      43                 : #include "nsString.h"
      44                 : #include "nsReadableUtils.h"
      45                 : #include "nsXPIDLString.h"
      46                 : #include "nsPrimitiveHelpers.h"
      47                 : #include "nsICharsetConverterManager.h"
      48                 : #include "nsIServiceManager.h"
      49                 : #include "nsImageToPixbuf.h"
      50                 : #include "nsStringStream.h"
      51                 : #include "nsIObserverService.h"
      52                 : #include "mozilla/Services.h"
      53                 : 
      54                 : #include "imgIContainer.h"
      55                 : 
      56                 : #include <gtk/gtk.h>
      57                 : 
      58                 : // For manipulation of the X event queue
      59                 : #include <X11/Xlib.h>
      60                 : #include <gdk/gdkx.h>
      61                 : #include <sys/time.h>
      62                 : #include <sys/types.h>
      63                 : #include <unistd.h>
      64                 : 
      65                 : using namespace mozilla;
      66                 : 
      67                 : // Callback when someone asks us for the data
      68                 : void
      69                 : clipboard_get_cb(GtkClipboard *aGtkClipboard,
      70                 :                  GtkSelectionData *aSelectionData,
      71                 :                  guint info,
      72                 :                  gpointer user_data);
      73                 : 
      74                 : // Callback when someone asks us to clear a clipboard
      75                 : void
      76                 : clipboard_clear_cb(GtkClipboard *aGtkClipboard,
      77                 :                    gpointer user_data);
      78                 :                    
      79                 : static void
      80                 : ConvertHTMLtoUCS2          (guchar             *data,
      81                 :                             PRInt32             dataLength,
      82                 :                             PRUnichar         **unicodeData,
      83                 :                             PRInt32            &outUnicodeLen);
      84                 : 
      85                 : static void
      86                 : GetHTMLCharset             (guchar * data, PRInt32 dataLength, nsCString& str);
      87                 : 
      88                 : 
      89                 : // Our own versions of gtk_clipboard_wait_for_contents and
      90                 : // gtk_clipboard_wait_for_text, which don't run the event loop while
      91                 : // waiting for the data.  This prevents a lot of problems related to
      92                 : // dispatching events at unexpected times.
      93                 : 
      94                 : static GtkSelectionData *
      95                 : wait_for_contents          (GtkClipboard *clipboard, GdkAtom target);
      96                 : 
      97                 : static gchar *
      98                 : wait_for_text              (GtkClipboard *clipboard);
      99                 : 
     100                 : static Bool
     101                 : checkEventProc(Display *display, XEvent *event, XPointer arg);
     102                 : 
     103                 : struct retrieval_context
     104                 : {
     105                 :     bool completed;
     106                 :     bool timed_out;
     107                 :     void    *data;
     108                 : 
     109               0 :     retrieval_context()
     110                 :       : completed(false),
     111                 :         timed_out(false),
     112               0 :         data(nsnull)
     113               0 :     { }
     114                 : };
     115                 : 
     116                 : static bool
     117                 : wait_for_retrieval(GtkClipboard *clipboard, retrieval_context *transferData);
     118                 : 
     119                 : static void
     120                 : clipboard_contents_received(GtkClipboard     *clipboard,
     121                 :                             GtkSelectionData *selection_data,
     122                 :                             gpointer          data);
     123                 : 
     124                 : static void
     125                 : clipboard_text_received(GtkClipboard *clipboard,
     126                 :                         const gchar  *text,
     127                 :                         gpointer      data);
     128                 : 
     129               0 : nsClipboard::nsClipboard()
     130                 : {
     131               0 : }
     132                 : 
     133               0 : nsClipboard::~nsClipboard()
     134                 : {
     135                 :     // We have to clear clipboard before gdk_display_close() call.
     136                 :     // See bug 531580 for details.
     137               0 :     if (mGlobalTransferable) {
     138               0 :         gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
     139                 :     }
     140               0 :     if (mSelectionTransferable) {
     141               0 :         gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
     142                 :     }
     143               0 : }
     144                 : 
     145               0 : NS_IMPL_ISUPPORTS1(nsClipboard, nsIClipboard)
     146                 : 
     147                 : nsresult
     148               0 : nsClipboard::Init(void)
     149                 : {
     150               0 :     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     151               0 :     if (!os)
     152               0 :       return NS_ERROR_FAILURE;
     153                 : 
     154               0 :     os->AddObserver(this, "quit-application", false);
     155                 : 
     156               0 :     return NS_OK;
     157                 : }
     158                 : 
     159                 : NS_IMETHODIMP
     160               0 : nsClipboard::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
     161                 : {
     162               0 :     if (strcmp(aTopic, "quit-application") == 0) {
     163                 :         // application is going to quit, save clipboard content
     164               0 :         Store();
     165                 :     }
     166               0 :     return NS_OK;
     167                 : }
     168                 : 
     169                 : nsresult
     170               0 : nsClipboard::Store(void)
     171                 : {
     172                 :     // Ask the clipboard manager to store the current clipboard content
     173               0 :     if (mGlobalTransferable) {
     174               0 :         GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
     175               0 :         gtk_clipboard_store(clipboard);
     176                 :     }
     177               0 :     return NS_OK;
     178                 : }
     179                 : 
     180                 : NS_IMETHODIMP
     181               0 : nsClipboard::SetData(nsITransferable *aTransferable,
     182                 :                      nsIClipboardOwner *aOwner, PRInt32 aWhichClipboard)
     183                 : {
     184                 :     // See if we can short cut
     185               0 :     if ((aWhichClipboard == kGlobalClipboard &&
     186               0 :          aTransferable == mGlobalTransferable.get() &&
     187               0 :          aOwner == mGlobalOwner.get()) ||
     188                 :         (aWhichClipboard == kSelectionClipboard &&
     189               0 :          aTransferable == mSelectionTransferable.get() &&
     190               0 :          aOwner == mSelectionOwner.get())) {
     191               0 :         return NS_OK;
     192                 :     }
     193                 : 
     194                 :     nsresult rv;
     195               0 :     if (!mPrivacyHandler) {
     196               0 :         rv = NS_NewClipboardPrivacyHandler(getter_AddRefs(mPrivacyHandler));
     197               0 :         NS_ENSURE_SUCCESS(rv, rv);
     198                 :     }
     199               0 :     rv = mPrivacyHandler->PrepareDataForClipboard(aTransferable);
     200               0 :     NS_ENSURE_SUCCESS(rv, rv);
     201                 : 
     202                 :     // Clear out the clipboard in order to set the new data
     203               0 :     EmptyClipboard(aWhichClipboard);
     204                 : 
     205                 :     // List of suported targets
     206               0 :     GtkTargetList *list = gtk_target_list_new(NULL, 0);
     207                 : 
     208                 :     // Get the types of supported flavors
     209               0 :     nsCOMPtr<nsISupportsArray> flavors;
     210                 : 
     211               0 :     rv = aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavors));
     212               0 :     if (!flavors || NS_FAILED(rv))
     213               0 :         return NS_ERROR_FAILURE;
     214                 : 
     215                 :     // Add all the flavors to this widget's supported type.
     216               0 :     bool imagesAdded = false;
     217                 :     PRUint32 count;
     218               0 :     flavors->Count(&count);
     219               0 :     for (PRUint32 i=0; i < count; i++) {
     220               0 :         nsCOMPtr<nsISupports> tastesLike;
     221               0 :         flavors->GetElementAt(i, getter_AddRefs(tastesLike));
     222               0 :         nsCOMPtr<nsISupportsCString> flavor = do_QueryInterface(tastesLike);
     223                 : 
     224               0 :         if (flavor) {
     225               0 :             nsXPIDLCString flavorStr;
     226               0 :             flavor->ToString(getter_Copies(flavorStr));
     227                 : 
     228                 :             // special case text/unicode since we can handle all of
     229                 :             // the string types
     230               0 :             if (!strcmp(flavorStr, kUnicodeMime)) {
     231               0 :                 gtk_target_list_add(list, gdk_atom_intern("UTF8_STRING", FALSE), 0, 0);
     232               0 :                 gtk_target_list_add(list, gdk_atom_intern("COMPOUND_TEXT", FALSE), 0, 0);
     233               0 :                 gtk_target_list_add(list, gdk_atom_intern("TEXT", FALSE), 0, 0);
     234               0 :                 gtk_target_list_add(list, GDK_SELECTION_TYPE_STRING, 0, 0);
     235               0 :                 continue;
     236                 :             }
     237                 : 
     238               0 :             if (flavorStr.EqualsLiteral(kNativeImageMime) ||
     239               0 :                 flavorStr.EqualsLiteral(kPNGImageMime) ||
     240               0 :                 flavorStr.EqualsLiteral(kJPEGImageMime) ||
     241               0 :                 flavorStr.EqualsLiteral(kGIFImageMime)) {
     242                 :                 // don't bother adding image targets twice
     243               0 :                 if (!imagesAdded) {
     244                 :                     // accept any writable image type
     245               0 :                     gtk_target_list_add_image_targets(list, 0, TRUE);
     246               0 :                     imagesAdded = true;
     247                 :                 }
     248               0 :                 continue;
     249                 :             }
     250                 : 
     251                 :             // Add this to our list of valid targets
     252               0 :             GdkAtom atom = gdk_atom_intern(flavorStr, FALSE);
     253               0 :             gtk_target_list_add(list, atom, 0, 0);
     254                 :         }
     255                 :     }
     256                 :     
     257                 :     // Get GTK clipboard (CLIPBOARD or PRIMARY)
     258               0 :     GtkClipboard *gtkClipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
     259                 :   
     260                 :     gint numTargets;
     261               0 :     GtkTargetEntry *gtkTargets = gtk_target_table_new_from_list(list, &numTargets);
     262                 :           
     263                 :     // Set getcallback and request to store data after an application exit
     264               0 :     if (gtk_clipboard_set_with_data(gtkClipboard, gtkTargets, numTargets, 
     265               0 :                                     clipboard_get_cb, clipboard_clear_cb, this))
     266                 :     {
     267                 :         // We managed to set-up the clipboard so update internal state
     268                 :         // We have to set it now because gtk_clipboard_set_with_data() calls clipboard_clear_cb()
     269                 :         // which reset our internal state 
     270               0 :         if (aWhichClipboard == kSelectionClipboard) {
     271               0 :             mSelectionOwner = aOwner;
     272               0 :             mSelectionTransferable = aTransferable;
     273                 :         }
     274                 :         else {
     275               0 :             mGlobalOwner = aOwner;
     276               0 :             mGlobalTransferable = aTransferable;
     277               0 :             gtk_clipboard_set_can_store(gtkClipboard, gtkTargets, numTargets);
     278                 :         }
     279                 : 
     280               0 :         rv = NS_OK;
     281                 :     }
     282                 :     else {  
     283               0 :         rv = NS_ERROR_FAILURE;
     284                 :     }
     285                 : 
     286               0 :     gtk_target_table_free(gtkTargets, numTargets);
     287               0 :     gtk_target_list_unref(list);
     288                 :   
     289               0 :     return rv;
     290                 : }
     291                 : 
     292                 : NS_IMETHODIMP
     293               0 : nsClipboard::GetData(nsITransferable *aTransferable, PRInt32 aWhichClipboard)
     294                 : {
     295               0 :     if (!aTransferable)
     296               0 :         return NS_ERROR_FAILURE;
     297                 : 
     298                 :     GtkClipboard *clipboard;
     299               0 :     clipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
     300                 : 
     301               0 :     guchar        *data = NULL;
     302               0 :     gint           length = 0;
     303               0 :     bool           foundData = false;
     304               0 :     nsCAutoString  foundFlavor;
     305                 : 
     306                 :     // Get a list of flavors this transferable can import
     307               0 :     nsCOMPtr<nsISupportsArray> flavors;
     308                 :     nsresult rv;
     309               0 :     rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavors));
     310               0 :     if (!flavors || NS_FAILED(rv))
     311               0 :         return NS_ERROR_FAILURE;
     312                 : 
     313                 :     PRUint32 count;
     314               0 :     flavors->Count(&count);
     315               0 :     for (PRUint32 i=0; i < count; i++) {
     316               0 :         nsCOMPtr<nsISupports> genericFlavor;
     317               0 :         flavors->GetElementAt(i, getter_AddRefs(genericFlavor));
     318                 : 
     319               0 :         nsCOMPtr<nsISupportsCString> currentFlavor;
     320               0 :         currentFlavor = do_QueryInterface(genericFlavor);
     321                 : 
     322               0 :         if (currentFlavor) {
     323               0 :             nsXPIDLCString flavorStr;
     324               0 :             currentFlavor->ToString(getter_Copies(flavorStr));
     325                 : 
     326                 :             // Special case text/unicode since we can convert any
     327                 :             // string into text/unicode
     328               0 :             if (!strcmp(flavorStr, kUnicodeMime)) {
     329               0 :                 gchar* new_text = wait_for_text(clipboard);
     330               0 :                 if (new_text) {
     331                 :                     // Convert utf-8 into our unicode format.
     332               0 :                     NS_ConvertUTF8toUTF16 ucs2string(new_text);
     333               0 :                     data = (guchar *)ToNewUnicode(ucs2string);
     334               0 :                     length = ucs2string.Length() * 2;
     335               0 :                     g_free(new_text);
     336               0 :                     foundData = true;
     337               0 :                     foundFlavor = kUnicodeMime;
     338                 :                     break;
     339                 :                 }
     340                 :                 // If the type was text/unicode and we couldn't get
     341                 :                 // text off the clipboard, run the next loop
     342                 :                 // iteration.
     343               0 :                 continue;
     344                 :             }
     345                 : 
     346                 :             // For images, we must wrap the data in an nsIInputStream then return instead of break,
     347                 :             // because that code below won't help us.
     348               0 :             if (!strcmp(flavorStr, kJPEGImageMime) || !strcmp(flavorStr, kPNGImageMime) || !strcmp(flavorStr, kGIFImageMime)) {
     349                 :                 GdkAtom atom;
     350               0 :                 if (!strcmp(flavorStr, kJPEGImageMime)) // This is image/jpg, but X only understands image/jpeg
     351               0 :                     atom = gdk_atom_intern("image/jpeg", FALSE);
     352                 :                 else
     353               0 :                     atom = gdk_atom_intern(flavorStr, FALSE);
     354                 : 
     355               0 :                 GtkSelectionData *selectionData = wait_for_contents(clipboard, atom);
     356               0 :                 if (!selectionData)
     357               0 :                     continue;
     358                 : 
     359               0 :                 nsCOMPtr<nsIInputStream> byteStream;
     360               0 :                 NS_NewByteInputStream(getter_AddRefs(byteStream), (const char*)selectionData->data,
     361               0 :                                       selectionData->length, NS_ASSIGNMENT_COPY);
     362               0 :                 aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*));
     363               0 :                 gtk_selection_data_free(selectionData);
     364               0 :                 return NS_OK;
     365                 :             }
     366                 : 
     367                 :             // Get the atom for this type and try to request it off
     368                 :             // the clipboard.
     369               0 :             GdkAtom atom = gdk_atom_intern(flavorStr, FALSE);
     370                 :             GtkSelectionData *selectionData;
     371               0 :             selectionData = wait_for_contents(clipboard, atom);
     372               0 :             if (selectionData) {
     373               0 :                 length = selectionData->length;
     374                 :                 // Special case text/html since we can convert into UCS2
     375               0 :                 if (!strcmp(flavorStr, kHTMLMime)) {
     376               0 :                     PRUnichar* htmlBody= nsnull;
     377               0 :                     PRInt32 htmlBodyLen = 0;
     378                 :                     // Convert text/html into our unicode format
     379                 :                     ConvertHTMLtoUCS2((guchar *)selectionData->data, length,
     380               0 :                                       &htmlBody, htmlBodyLen);
     381                 :                     // Try next data format?
     382               0 :                     if (!htmlBodyLen)
     383               0 :                         continue;
     384               0 :                     data = (guchar *)htmlBody;
     385               0 :                     length = htmlBodyLen * 2;
     386                 :                 } else {
     387               0 :                     data = (guchar *)nsMemory::Alloc(length);
     388               0 :                     if (!data)
     389                 :                         break;
     390               0 :                     memcpy(data, selectionData->data, length);
     391                 :                 }
     392               0 :                 foundData = true;
     393               0 :                 foundFlavor = flavorStr;
     394                 :                 break;
     395                 :             }
     396                 :         }
     397                 :     }
     398                 : 
     399               0 :     if (foundData) {
     400               0 :         nsCOMPtr<nsISupports> wrapper;
     401                 :         nsPrimitiveHelpers::CreatePrimitiveForData(foundFlavor.get(),
     402                 :                                                    data, length,
     403               0 :                                                    getter_AddRefs(wrapper));
     404                 :         aTransferable->SetTransferData(foundFlavor.get(),
     405               0 :                                        wrapper, length);
     406                 :     }
     407                 : 
     408               0 :     if (data)
     409               0 :         nsMemory::Free(data);
     410                 : 
     411               0 :     return NS_OK;
     412                 : }
     413                 : 
     414                 : NS_IMETHODIMP
     415               0 : nsClipboard::EmptyClipboard(PRInt32 aWhichClipboard)
     416                 : {
     417               0 :     if (aWhichClipboard == kSelectionClipboard) {
     418               0 :         if (mSelectionOwner) {
     419               0 :             mSelectionOwner->LosingOwnership(mSelectionTransferable);
     420               0 :             mSelectionOwner = nsnull;
     421                 :         }
     422               0 :         mSelectionTransferable = nsnull;
     423                 :     }
     424                 :     else {
     425               0 :         if (mGlobalOwner) {
     426               0 :             mGlobalOwner->LosingOwnership(mGlobalTransferable);
     427               0 :             mGlobalOwner = nsnull;
     428                 :         }
     429               0 :         mGlobalTransferable = nsnull;
     430                 :     }
     431                 : 
     432               0 :     return NS_OK;
     433                 : }
     434                 : 
     435                 : NS_IMETHODIMP
     436               0 : nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, PRUint32 aLength,
     437                 :                                     PRInt32 aWhichClipboard, bool *_retval)
     438                 : {
     439               0 :     if (!aFlavorList || !_retval)
     440               0 :         return NS_ERROR_NULL_POINTER;
     441                 : 
     442               0 :     *_retval = false;
     443                 : 
     444                 :     GtkSelectionData *selection_data =
     445               0 :         GetTargets(GetSelectionAtom(aWhichClipboard));
     446               0 :     if (!selection_data)
     447               0 :         return NS_OK;
     448                 : 
     449               0 :     gint n_targets = 0;
     450               0 :     GdkAtom *targets = NULL;
     451                 : 
     452               0 :     if (!gtk_selection_data_get_targets(selection_data, 
     453               0 :                                         &targets, &n_targets) ||
     454               0 :         !n_targets)
     455               0 :         return NS_OK;
     456                 : 
     457                 :     // Walk through the provided types and try to match it to a
     458                 :     // provided type.
     459               0 :     for (PRUint32 i = 0; i < aLength && !*_retval; i++) {
     460                 :         // We special case text/unicode here.
     461               0 :         if (!strcmp(aFlavorList[i], kUnicodeMime) && 
     462               0 :             gtk_selection_data_targets_include_text(selection_data)) {
     463               0 :             *_retval = true;
     464               0 :             break;
     465                 :         }
     466                 : 
     467               0 :         for (PRInt32 j = 0; j < n_targets; j++) {
     468               0 :             gchar *atom_name = gdk_atom_name(targets[j]);
     469               0 :             if (!atom_name)
     470               0 :                 continue;
     471                 : 
     472               0 :             if (!strcmp(atom_name, aFlavorList[i]))
     473               0 :                 *_retval = true;
     474                 : 
     475                 :             // X clipboard wants image/jpeg, not image/jpg
     476               0 :             if (!strcmp(aFlavorList[i], kJPEGImageMime) && !strcmp(atom_name, "image/jpeg"))
     477               0 :                 *_retval = true;
     478                 : 
     479               0 :             g_free(atom_name);
     480                 : 
     481               0 :             if (*_retval)
     482               0 :                 break;
     483                 :         }
     484                 :     }
     485               0 :     gtk_selection_data_free(selection_data);
     486               0 :     g_free(targets);
     487                 : 
     488               0 :     return NS_OK;
     489                 : }
     490                 : 
     491                 : NS_IMETHODIMP
     492               0 : nsClipboard::SupportsSelectionClipboard(bool *_retval)
     493                 : {
     494               0 :     *_retval = true; // yeah, unix supports the selection clipboard
     495               0 :     return NS_OK;
     496                 : }
     497                 : 
     498                 : /* static */
     499                 : GdkAtom
     500               0 : nsClipboard::GetSelectionAtom(PRInt32 aWhichClipboard)
     501                 : {
     502               0 :     if (aWhichClipboard == kGlobalClipboard)
     503               0 :         return GDK_SELECTION_CLIPBOARD;
     504                 : 
     505               0 :     return GDK_SELECTION_PRIMARY;
     506                 : }
     507                 : 
     508                 : /* static */
     509                 : GtkSelectionData *
     510               0 : nsClipboard::GetTargets(GdkAtom aWhichClipboard)
     511                 : {
     512               0 :     GtkClipboard *clipboard = gtk_clipboard_get(aWhichClipboard);
     513               0 :     return wait_for_contents(clipboard, gdk_atom_intern("TARGETS", FALSE));
     514                 : }
     515                 : 
     516                 : nsITransferable *
     517               0 : nsClipboard::GetTransferable(PRInt32 aWhichClipboard)
     518                 : {
     519                 :     nsITransferable *retval;
     520                 : 
     521               0 :     if (aWhichClipboard == kSelectionClipboard)
     522               0 :         retval = mSelectionTransferable.get();
     523                 :     else
     524               0 :         retval = mGlobalTransferable.get();
     525                 :         
     526               0 :     return retval;
     527                 : }
     528                 : 
     529                 : void
     530               0 : nsClipboard::SelectionGetEvent(GtkClipboard     *aClipboard,
     531                 :                                GtkSelectionData *aSelectionData)
     532                 : {
     533                 :     // Someone has asked us to hand them something.  The first thing
     534                 :     // that we want to do is see if that something includes text.  If
     535                 :     // it does, try to give it text/unicode after converting it to
     536                 :     // utf-8.
     537                 : 
     538                 :     PRInt32 whichClipboard;
     539                 : 
     540                 :     // which clipboard?
     541               0 :     if (aSelectionData->selection == GDK_SELECTION_PRIMARY)
     542               0 :         whichClipboard = kSelectionClipboard;
     543               0 :     else if (aSelectionData->selection == GDK_SELECTION_CLIPBOARD)
     544               0 :         whichClipboard = kGlobalClipboard;
     545                 :     else
     546               0 :         return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
     547                 : 
     548               0 :     nsCOMPtr<nsITransferable> trans = GetTransferable(whichClipboard);
     549               0 :     if (!trans) {
     550                 :       // We have nothing to serve
     551                 : #ifdef DEBUG_CLIPBOARD
     552                 :       printf("nsClipboard::SelectionGetEvent() - %s clipboard is empty!\n",
     553                 :              whichClipboard == kSelectionClipboard ? "Selection" : "Global");
     554                 : #endif
     555                 :       return;
     556                 :     }
     557                 : 
     558                 :     nsresult rv;
     559               0 :     nsCOMPtr<nsISupports> item;
     560                 :     PRUint32 len;
     561                 : 
     562                 :     // Check to see if the selection data includes any of the string
     563                 :     // types that we support.
     564               0 :     if (aSelectionData->target == gdk_atom_intern ("STRING", FALSE) ||
     565               0 :         aSelectionData->target == gdk_atom_intern ("TEXT", FALSE) ||
     566               0 :         aSelectionData->target == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
     567               0 :         aSelectionData->target == gdk_atom_intern ("UTF8_STRING", FALSE)) {
     568                 :         // Try to convert our internal type into a text string.  Get
     569                 :         // the transferable for this clipboard and try to get the
     570                 :         // text/unicode type for it.
     571               0 :         rv = trans->GetTransferData("text/unicode", getter_AddRefs(item),
     572               0 :                                     &len);
     573               0 :         if (!item || NS_FAILED(rv))
     574                 :             return;
     575                 :         
     576               0 :         nsCOMPtr<nsISupportsString> wideString;
     577               0 :         wideString = do_QueryInterface(item);
     578               0 :         if (!wideString)
     579                 :             return;
     580                 : 
     581               0 :         nsAutoString ucs2string;
     582               0 :         wideString->GetData(ucs2string);
     583               0 :         char *utf8string = ToNewUTF8String(ucs2string);
     584               0 :         if (!utf8string)
     585                 :             return;
     586                 :         
     587                 :         gtk_selection_data_set_text (aSelectionData, utf8string,
     588               0 :                                      strlen(utf8string));
     589                 : 
     590               0 :         nsMemory::Free(utf8string);
     591                 :         return;
     592                 :     }
     593                 : 
     594                 :     // Check to see if the selection data is an image type
     595               0 :     if (gtk_targets_include_image(&aSelectionData->target, 1, TRUE)) {
     596                 :         // Look through our transfer data for the image
     597                 :         static const char* const imageMimeTypes[] = {
     598                 :             kNativeImageMime, kPNGImageMime, kJPEGImageMime, kGIFImageMime };
     599               0 :         nsCOMPtr<nsISupports> item;
     600                 :         PRUint32 len;
     601               0 :         nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive;
     602               0 :         for (PRUint32 i = 0; !ptrPrimitive && i < ArrayLength(imageMimeTypes); i++) {
     603               0 :             rv = trans->GetTransferData(imageMimeTypes[i], getter_AddRefs(item), &len);
     604               0 :             ptrPrimitive = do_QueryInterface(item);
     605                 :         }
     606               0 :         if (!ptrPrimitive)
     607                 :             return;
     608                 : 
     609               0 :         nsCOMPtr<nsISupports> primitiveData;
     610               0 :         ptrPrimitive->GetData(getter_AddRefs(primitiveData));
     611               0 :         nsCOMPtr<imgIContainer> image(do_QueryInterface(primitiveData));
     612               0 :         if (!image) // Not getting an image for an image mime type!?
     613                 :             return;
     614                 : 
     615               0 :         GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(image);
     616               0 :         if (!pixbuf)
     617                 :             return;
     618                 : 
     619               0 :         gtk_selection_data_set_pixbuf(aSelectionData, pixbuf);
     620               0 :         g_object_unref(pixbuf);
     621                 :         return;
     622                 :     }
     623                 : 
     624                 :     // Try to match up the selection data target to something our
     625                 :     // transferable provides.
     626               0 :     gchar *target_name = gdk_atom_name(aSelectionData->target);
     627               0 :     if (!target_name)
     628                 :         return;
     629                 : 
     630               0 :     rv = trans->GetTransferData(target_name, getter_AddRefs(item), &len);
     631                 :     // nothing found?
     632               0 :     if (!item || NS_FAILED(rv)) {
     633               0 :         g_free(target_name);
     634                 :         return;
     635                 :     }
     636                 : 
     637               0 :     void *primitive_data = nsnull;
     638                 :     nsPrimitiveHelpers::CreateDataFromPrimitive(target_name, item,
     639               0 :                                                 &primitive_data, len);
     640                 : 
     641               0 :     if (primitive_data) {
     642                 :         // Check to see if the selection data is text/html
     643               0 :         if (aSelectionData->target == gdk_atom_intern (kHTMLMime, FALSE)) {
     644                 :             /*
     645                 :              * "text/html" can be encoded UCS2. It is recommended that
     646                 :              * documents transmitted as UCS2 always begin with a ZERO-WIDTH
     647                 :              * NON-BREAKING SPACE character (hexadecimal FEFF, also called
     648                 :              * Byte Order Mark (BOM)). Adding BOM can help other app to
     649                 :              * detect mozilla use UCS2 encoding when copy-paste.
     650                 :              */
     651                 :             guchar *buffer = (guchar *)
     652               0 :                     nsMemory::Alloc((len * sizeof(guchar)) + sizeof(PRUnichar));
     653               0 :             if (!buffer)
     654                 :                 return;
     655               0 :             PRUnichar prefix = 0xFEFF;
     656               0 :             memcpy(buffer, &prefix, sizeof(prefix));
     657               0 :             memcpy(buffer + sizeof(prefix), primitive_data, len);
     658               0 :             nsMemory::Free((guchar *)primitive_data);
     659               0 :             primitive_data = (guchar *)buffer;
     660               0 :             len += sizeof(prefix);
     661                 :         }
     662                 :   
     663                 :         gtk_selection_data_set(aSelectionData, aSelectionData->target,
     664                 :                                8, /* 8 bits in a unit */
     665               0 :                                (const guchar *)primitive_data, len);
     666               0 :         nsMemory::Free(primitive_data);
     667                 :     }
     668                 : 
     669               0 :     g_free(target_name);
     670                 :                            
     671                 : }
     672                 : 
     673                 : void
     674               0 : nsClipboard::SelectionClearEvent(GtkClipboard *aGtkClipboard)
     675                 : {
     676                 :     PRInt32 whichClipboard;
     677                 : 
     678                 :     // which clipboard?
     679               0 :     if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_PRIMARY))
     680               0 :         whichClipboard = kSelectionClipboard;
     681               0 :     else if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_CLIPBOARD))
     682               0 :         whichClipboard = kGlobalClipboard;
     683                 :     else
     684               0 :         return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
     685                 : 
     686               0 :     EmptyClipboard(whichClipboard);
     687                 : }
     688                 : 
     689                 : void
     690               0 : clipboard_get_cb(GtkClipboard *aGtkClipboard,
     691                 :                  GtkSelectionData *aSelectionData,
     692                 :                  guint info,
     693                 :                  gpointer user_data)
     694                 : {
     695               0 :     nsClipboard *aClipboard = static_cast<nsClipboard *>(user_data);
     696               0 :     aClipboard->SelectionGetEvent(aGtkClipboard, aSelectionData);
     697               0 : }
     698                 : 
     699                 : void
     700               0 : clipboard_clear_cb(GtkClipboard *aGtkClipboard,
     701                 :                    gpointer user_data)
     702                 : {
     703               0 :     nsClipboard *aClipboard = static_cast<nsClipboard *>(user_data);
     704               0 :     aClipboard->SelectionClearEvent(aGtkClipboard);
     705               0 : }
     706                 : 
     707                 : /*
     708                 :  * when copy-paste, mozilla wants data encoded using UCS2,
     709                 :  * other app such as StarOffice use "text/html"(RFC2854).
     710                 :  * This function convert data(got from GTK clipboard)
     711                 :  * to data mozilla wanted.
     712                 :  *
     713                 :  * data from GTK clipboard can be 3 forms:
     714                 :  *  1. From current mozilla
     715                 :  *     "text/html", charset = utf-16
     716                 :  *  2. From old version mozilla or mozilla-based app
     717                 :  *     content("body" only), charset = utf-16
     718                 :  *  3. From other app who use "text/html" when copy-paste
     719                 :  *     "text/html", has "charset" info
     720                 :  *
     721                 :  * data      : got from GTK clipboard
     722                 :  * dataLength: got from GTK clipboard
     723                 :  * body      : pass to Mozilla
     724                 :  * bodyLength: pass to Mozilla
     725                 :  */
     726               0 : void ConvertHTMLtoUCS2(guchar * data, PRInt32 dataLength,
     727                 :                        PRUnichar** unicodeData, PRInt32& outUnicodeLen)
     728                 : {
     729               0 :     nsCAutoString charset;
     730               0 :     GetHTMLCharset(data, dataLength, charset);// get charset of HTML
     731               0 :     if (charset.EqualsLiteral("UTF-16")) {//current mozilla
     732               0 :         outUnicodeLen = (dataLength / 2) - 1;
     733                 :         *unicodeData = reinterpret_cast<PRUnichar*>
     734                 :                                        (nsMemory::Alloc((outUnicodeLen + sizeof('\0')) *
     735               0 :                        sizeof(PRUnichar)));
     736               0 :         if (*unicodeData) {
     737                 :             memcpy(*unicodeData, data + sizeof(PRUnichar),
     738               0 :                    outUnicodeLen * sizeof(PRUnichar));
     739               0 :             (*unicodeData)[outUnicodeLen] = '\0';
     740                 :         }
     741               0 :     } else if (charset.EqualsLiteral("UNKNOWN")) {
     742               0 :         outUnicodeLen = 0;
     743                 :         return;
     744                 :     } else {
     745                 :         // app which use "text/html" to copy&paste
     746               0 :         nsCOMPtr<nsIUnicodeDecoder> decoder;
     747                 :         nsresult rv;
     748                 :         // get the decoder
     749                 :         nsCOMPtr<nsICharsetConverterManager> ccm =
     750               0 :             do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
     751               0 :         if (NS_FAILED(rv)) {
     752                 : #ifdef DEBUG_CLIPBOARD
     753                 :             g_print("        can't get CHARSET CONVERTER MANAGER service\n");
     754                 : #endif
     755               0 :             outUnicodeLen = 0;
     756                 :             return;
     757                 :         }
     758               0 :         rv = ccm->GetUnicodeDecoder(charset.get(), getter_AddRefs(decoder));
     759               0 :         if (NS_FAILED(rv)) {
     760                 : #ifdef DEBUG_CLIPBOARD
     761                 :             g_print("        get unicode decoder error\n");
     762                 : #endif
     763               0 :             outUnicodeLen = 0;
     764                 :             return;
     765                 :         }
     766                 :         // converting
     767               0 :         decoder->GetMaxLength((const char *)data, dataLength, &outUnicodeLen);
     768                 :         // |outUnicodeLen| is number of chars
     769               0 :         if (outUnicodeLen) {
     770                 :             *unicodeData = reinterpret_cast<PRUnichar*>
     771                 :                                            (nsMemory::Alloc((outUnicodeLen + sizeof('\0')) *
     772               0 :                            sizeof(PRUnichar)));
     773               0 :             if (*unicodeData) {
     774               0 :                 PRInt32 numberTmp = dataLength;
     775               0 :                 decoder->Convert((const char *)data, &numberTmp,
     776               0 :                                  *unicodeData, &outUnicodeLen);
     777                 : #ifdef DEBUG_CLIPBOARD
     778                 :                 if (numberTmp != dataLength)
     779                 :                     printf("didn't consume all the bytes\n");
     780                 : #endif
     781                 :                 // null terminate. Convert() doesn't do it for us
     782               0 :                 (*unicodeData)[outUnicodeLen] = '\0';
     783                 :             }
     784                 :         } // if valid length
     785                 :     }
     786                 : }
     787                 : 
     788                 : /*
     789                 :  * get "charset" information from clipboard data
     790                 :  * return value can be:
     791                 :  *  1. "UTF-16":      mozilla or "text/html" with "charset=utf-16"
     792                 :  *  2. "UNKNOWN":     mozilla can't detect what encode it use
     793                 :  *  3. other:         "text/html" with other charset than utf-16
     794                 :  */
     795               0 : void GetHTMLCharset(guchar * data, PRInt32 dataLength, nsCString& str)
     796                 : {
     797                 :     // if detect "FFFE" or "FEFF", assume UTF-16
     798               0 :     PRUnichar* beginChar =  (PRUnichar*)data;
     799               0 :     if ((beginChar[0] == 0xFFFE) || (beginChar[0] == 0xFEFF)) {
     800               0 :         str.AssignLiteral("UTF-16");
     801               0 :         return;
     802                 :     }
     803                 :     // no "FFFE" and "FEFF", assume ASCII first to find "charset" info
     804               0 :     const nsDependentCString htmlStr((const char *)data, dataLength);
     805               0 :     nsACString::const_iterator start, end;
     806               0 :     htmlStr.BeginReading(start);
     807               0 :     htmlStr.EndReading(end);
     808               0 :     nsACString::const_iterator valueStart(start), valueEnd(start);
     809                 : 
     810               0 :     if (CaseInsensitiveFindInReadable(
     811               0 :         NS_LITERAL_CSTRING("CONTENT=\"text/html;"),
     812               0 :         start, end)) {
     813               0 :         start = end;
     814               0 :         htmlStr.EndReading(end);
     815                 : 
     816               0 :         if (CaseInsensitiveFindInReadable(
     817               0 :             NS_LITERAL_CSTRING("charset="),
     818               0 :             start, end)) {
     819               0 :             valueStart = end;
     820               0 :             start = end;
     821               0 :             htmlStr.EndReading(end);
     822                 :           
     823               0 :             if (FindCharInReadable('"', start, end))
     824               0 :                 valueEnd = start;
     825                 :         }
     826                 :     }
     827                 :     // find "charset" in HTML
     828               0 :     if (valueStart != valueEnd) {
     829               0 :         str = Substring(valueStart, valueEnd);
     830               0 :         ToUpperCase(str);
     831                 : #ifdef DEBUG_CLIPBOARD
     832                 :         printf("Charset of HTML = %s\n", charsetUpperStr.get());
     833                 : #endif
     834                 :         return;
     835                 :     }
     836               0 :     str.AssignLiteral("UNKNOWN");
     837                 : }
     838                 : 
     839                 : static void
     840               0 : DispatchSelectionNotifyEvent(GtkWidget *widget, XEvent *xevent)
     841                 : {
     842                 :     GdkEvent event;
     843               0 :     event.selection.type = GDK_SELECTION_NOTIFY;
     844               0 :     event.selection.window = widget->window;
     845               0 :     event.selection.selection = gdk_x11_xatom_to_atom(xevent->xselection.selection);
     846               0 :     event.selection.target = gdk_x11_xatom_to_atom(xevent->xselection.target);
     847               0 :     event.selection.property = gdk_x11_xatom_to_atom(xevent->xselection.property);
     848               0 :     event.selection.time = xevent->xselection.time;
     849                 : 
     850               0 :     gtk_widget_event(widget, &event);
     851               0 : }
     852                 : 
     853                 : static void
     854               0 : DispatchPropertyNotifyEvent(GtkWidget *widget, XEvent *xevent)
     855                 : {
     856               0 :     if (((GdkWindowObject *) widget->window)->event_mask & GDK_PROPERTY_CHANGE_MASK) {
     857                 :         GdkEvent event;
     858               0 :         event.property.type = GDK_PROPERTY_NOTIFY;
     859               0 :         event.property.window = widget->window;
     860               0 :         event.property.atom = gdk_x11_xatom_to_atom(xevent->xproperty.atom);
     861               0 :         event.property.time = xevent->xproperty.time;
     862               0 :         event.property.state = xevent->xproperty.state;
     863                 : 
     864               0 :         gtk_widget_event(widget, &event);
     865                 :     }
     866               0 : }
     867                 : 
     868                 : struct checkEventContext
     869                 : {
     870                 :     GtkWidget *cbWidget;
     871                 :     Atom       selAtom;
     872                 : };
     873                 : 
     874                 : static Bool
     875               0 : checkEventProc(Display *display, XEvent *event, XPointer arg)
     876                 : {
     877               0 :     checkEventContext *context = (checkEventContext *) arg;
     878                 : 
     879               0 :     if (event->xany.type == SelectionNotify ||
     880                 :         (event->xany.type == PropertyNotify &&
     881                 :          event->xproperty.atom == context->selAtom)) {
     882                 : 
     883               0 :         GdkWindow *cbWindow = gdk_window_lookup(event->xany.window);
     884               0 :         if (cbWindow) {
     885               0 :             GtkWidget *cbWidget = NULL;
     886               0 :             gdk_window_get_user_data(cbWindow, (gpointer *)&cbWidget);
     887               0 :             if (cbWidget && GTK_IS_WIDGET(cbWidget)) {
     888               0 :                 context->cbWidget = cbWidget;
     889               0 :                 return True;
     890                 :             }
     891                 :         }
     892                 :     }
     893                 : 
     894               0 :     return False;
     895                 : }
     896                 : 
     897                 : // Idle timeout for receiving selection and property notify events (microsec)
     898                 : static const int kClipboardTimeout = 500000;
     899                 : 
     900                 : static bool
     901               0 : wait_for_retrieval(GtkClipboard *clipboard, retrieval_context *r_context)
     902                 : {
     903               0 :     if (r_context->completed)  // the request completed synchronously
     904               0 :         return true;
     905                 : 
     906               0 :     Display *xDisplay = GDK_DISPLAY();
     907                 :     checkEventContext context;
     908               0 :     context.cbWidget = NULL;
     909                 :     context.selAtom = gdk_x11_atom_to_xatom(gdk_atom_intern("GDK_SELECTION",
     910               0 :                                                             FALSE));
     911                 : 
     912                 :     // Send X events which are relevant to the ongoing selection retrieval
     913                 :     // to the clipboard widget.  Wait until either the operation completes, or
     914                 :     // we hit our timeout.  All other X events remain queued.
     915                 : 
     916                 :     int select_result;
     917                 : 
     918               0 :     int cnumber = ConnectionNumber(xDisplay);
     919                 :     fd_set select_set;
     920               0 :     FD_ZERO(&select_set);
     921               0 :     FD_SET(cnumber, &select_set);
     922               0 :     ++cnumber;
     923                 :     struct timeval tv;
     924                 : 
     925               0 :     do {
     926                 :         XEvent xevent;
     927                 : 
     928               0 :         while (XCheckIfEvent(xDisplay, &xevent, checkEventProc,
     929               0 :                              (XPointer) &context)) {
     930                 : 
     931               0 :             if (xevent.xany.type == SelectionNotify)
     932               0 :                 DispatchSelectionNotifyEvent(context.cbWidget, &xevent);
     933                 :             else
     934               0 :                 DispatchPropertyNotifyEvent(context.cbWidget, &xevent);
     935                 : 
     936               0 :             if (r_context->completed)
     937               0 :                 return true;
     938                 :         }
     939                 : 
     940               0 :         tv.tv_sec = 0;
     941               0 :         tv.tv_usec = kClipboardTimeout;
     942               0 :         select_result = select(cnumber, &select_set, NULL, NULL, &tv);
     943                 : 
     944                 :     } while (select_result == 1);
     945                 : 
     946                 : #ifdef DEBUG_CLIPBOARD
     947                 :     printf("exceeded clipboard timeout\n");
     948                 : #endif
     949               0 :     r_context->timed_out = true;
     950               0 :     return false;
     951                 : }
     952                 : 
     953                 : static void
     954               0 : clipboard_contents_received(GtkClipboard     *clipboard,
     955                 :                             GtkSelectionData *selection_data,
     956                 :                             gpointer          data)
     957                 : {
     958               0 :     retrieval_context *context = static_cast<retrieval_context *>(data);
     959               0 :     if (context->timed_out) {
     960               0 :         return;
     961                 :     }
     962                 : 
     963               0 :     context->completed = true;
     964                 : 
     965               0 :     if (selection_data->length >= 0)
     966               0 :         context->data = gtk_selection_data_copy(selection_data);
     967                 : }
     968                 : 
     969                 : 
     970                 : static GtkSelectionData *
     971               0 : wait_for_contents(GtkClipboard *clipboard, GdkAtom target)
     972                 : {
     973               0 :     retrieval_context context;
     974                 :     gtk_clipboard_request_contents(clipboard, target,
     975                 :                                    clipboard_contents_received,
     976               0 :                                    &context);
     977                 : 
     978               0 :     if (!wait_for_retrieval(clipboard, &context)) {
     979               0 :         return nsnull;
     980                 :     }
     981                 : 
     982               0 :     return static_cast<GtkSelectionData *>(context.data);
     983                 : }
     984                 : 
     985                 : static void
     986               0 : clipboard_text_received(GtkClipboard *clipboard,
     987                 :                         const gchar  *text,
     988                 :                         gpointer      data)
     989                 : {
     990               0 :     retrieval_context *context = static_cast<retrieval_context *>(data);
     991               0 :     if (context->timed_out) {
     992               0 :         return;
     993                 :     }
     994                 : 
     995               0 :     context->completed = true;
     996               0 :     context->data = g_strdup(text);
     997                 : }
     998                 : 
     999                 : static gchar *
    1000               0 : wait_for_text(GtkClipboard *clipboard)
    1001                 : {
    1002               0 :     retrieval_context context;
    1003               0 :     gtk_clipboard_request_text(clipboard, clipboard_text_received, &context);
    1004                 : 
    1005               0 :     if (!wait_for_retrieval(clipboard, &context)) {
    1006               0 :         return nsnull;
    1007                 :     }
    1008                 : 
    1009               0 :     return static_cast<gchar *>(context.data);
    1010                 : }

Generated by: LCOV version 1.7