LCOV - code coverage report
Current view: top level - widget/gtk - nsClipboard.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 26 505 5.1 %
Date: 2017-07-14 16:53:18 Functions: 8 42 19.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13