Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/gtk/nsClipboard.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsClipboardX11.h"
13
#if defined(MOZ_WAYLAND)
14
#include "nsClipboardWayland.h"
15
#endif
16
#include "HeadlessClipboard.h"
17
#include "nsSupportsPrimitives.h"
18
#include "nsString.h"
19
#include "nsReadableUtils.h"
20
#include "nsPrimitiveHelpers.h"
21
#include "nsIServiceManager.h"
22
#include "nsImageToPixbuf.h"
23
#include "nsStringStream.h"
24
#include "nsIObserverService.h"
25
#include "mozilla/Services.h"
26
#include "mozilla/RefPtr.h"
27
#include "mozilla/TimeStamp.h"
28
29
#include "imgIContainer.h"
30
31
#include <gtk/gtk.h>
32
#include <gtk/gtkx.h>
33
34
#include "mozilla/Encoding.h"
35
36
37
using namespace mozilla;
38
39
// Idle timeout for receiving selection and property notify events (microsec)
40
const int kClipboardTimeout = 500000;
41
42
// Callback when someone asks us for the data
43
void
44
clipboard_get_cb(GtkClipboard *aGtkClipboard,
45
                 GtkSelectionData *aSelectionData,
46
                 guint info,
47
                 gpointer user_data);
48
49
// Callback when someone asks us to clear a clipboard
50
void
51
clipboard_clear_cb(GtkClipboard *aGtkClipboard,
52
                   gpointer user_data);
53
54
static void
55
ConvertHTMLtoUCS2          (const char*         data,
56
                            int32_t             dataLength,
57
                            char16_t         **unicodeData,
58
                            int32_t            &outUnicodeLen);
59
60
static void
61
GetHTMLCharset             (const char* data, int32_t dataLength, nsCString& str);
62
63
GdkAtom
64
GetSelectionAtom(int32_t aWhichClipboard)
65
0
{
66
0
    if (aWhichClipboard == nsIClipboard::kGlobalClipboard)
67
0
        return GDK_SELECTION_CLIPBOARD;
68
0
69
0
    return GDK_SELECTION_PRIMARY;
70
0
}
71
72
nsClipboard::nsClipboard()
73
0
{
74
0
}
75
76
nsClipboard::~nsClipboard()
77
0
{
78
0
    // We have to clear clipboard before gdk_display_close() call.
79
0
    // See bug 531580 for details.
80
0
    if (mGlobalTransferable) {
81
0
        gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
82
0
    }
83
0
    if (mSelectionTransferable) {
84
0
        gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
85
0
    }
86
0
}
87
88
NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard)
89
90
nsresult
91
nsClipboard::Init(void)
92
0
{
93
0
    GdkDisplay *display = gdk_display_get_default();
94
0
95
0
    // Create a nsRetrievalContext. If there's no default display
96
0
    // create the X11 one as a fallback.
97
0
    if (!display || GDK_IS_X11_DISPLAY(display)) {
98
0
        mContext = new nsRetrievalContextX11();
99
#if defined(MOZ_WAYLAND)
100
    } else {
101
        mContext = new nsRetrievalContextWayland();
102
#endif
103
    }
104
0
    NS_ASSERTION(mContext, "Missing nsRetrievalContext for nsClipboard!");
105
0
106
0
    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
107
0
    if (os) {
108
0
        os->AddObserver(this, "quit-application", false);
109
0
        os->AddObserver(this, "xpcom-shutdown", false);
110
0
    }
111
0
112
0
    return NS_OK;
113
0
}
114
115
116
nsresult
117
nsClipboard::Store(void)
118
0
{
119
0
    if (mGlobalTransferable) {
120
0
        GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
121
0
        gtk_clipboard_store(clipboard);
122
0
    }
123
0
    return NS_OK;
124
0
}
125
126
NS_IMETHODIMP
127
nsClipboard::Observe(nsISupports *aSubject, const char *aTopic,
128
                     const char16_t *aData)
129
0
{
130
0
    Store();
131
0
    return NS_OK;
132
0
}
133
134
NS_IMETHODIMP
135
nsClipboard::SetData(nsITransferable *aTransferable,
136
                     nsIClipboardOwner *aOwner, int32_t aWhichClipboard)
137
0
{
138
0
    // See if we can short cut
139
0
    if ((aWhichClipboard == kGlobalClipboard &&
140
0
         aTransferable == mGlobalTransferable.get() &&
141
0
         aOwner == mGlobalOwner.get()) ||
142
0
        (aWhichClipboard == kSelectionClipboard &&
143
0
         aTransferable == mSelectionTransferable.get() &&
144
0
         aOwner == mSelectionOwner.get())) {
145
0
        return NS_OK;
146
0
    }
147
0
148
0
    // Clear out the clipboard in order to set the new data
149
0
    EmptyClipboard(aWhichClipboard);
150
0
151
0
    // List of suported targets
152
0
    GtkTargetList *list = gtk_target_list_new(nullptr, 0);
153
0
154
0
    // Get the types of supported flavors
155
0
    nsCOMPtr<nsIArray> flavors;
156
0
157
0
    nsresult rv =
158
0
        aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavors));
159
0
    if (!flavors || NS_FAILED(rv))
160
0
        return NS_ERROR_FAILURE;
161
0
162
0
    // Add all the flavors to this widget's supported type.
163
0
    bool imagesAdded = false;
164
0
    uint32_t count;
165
0
    flavors->GetLength(&count);
166
0
    for (uint32_t i=0; i < count; i++) {
167
0
        nsCOMPtr<nsISupportsCString> flavor = do_QueryElementAt(flavors, i);
168
0
169
0
        if (flavor) {
170
0
            nsCString flavorStr;
171
0
            flavor->ToString(getter_Copies(flavorStr));
172
0
173
0
            // special case text/unicode since we can handle all of
174
0
            // the string types
175
0
            if (flavorStr.EqualsLiteral(kUnicodeMime)) {
176
0
                gtk_target_list_add(list, gdk_atom_intern("UTF8_STRING", FALSE), 0, 0);
177
0
                gtk_target_list_add(list, gdk_atom_intern("COMPOUND_TEXT", FALSE), 0, 0);
178
0
                gtk_target_list_add(list, gdk_atom_intern("TEXT", FALSE), 0, 0);
179
0
                gtk_target_list_add(list, GDK_SELECTION_TYPE_STRING, 0, 0);
180
0
                continue;
181
0
            }
182
0
183
0
            if (flavorStr.EqualsLiteral(kNativeImageMime) ||
184
0
                flavorStr.EqualsLiteral(kPNGImageMime) ||
185
0
                flavorStr.EqualsLiteral(kJPEGImageMime) ||
186
0
                flavorStr.EqualsLiteral(kJPGImageMime) ||
187
0
                flavorStr.EqualsLiteral(kGIFImageMime)) {
188
0
                // don't bother adding image targets twice
189
0
                if (!imagesAdded) {
190
0
                    // accept any writable image type
191
0
                    gtk_target_list_add_image_targets(list, 0, TRUE);
192
0
                    imagesAdded = true;
193
0
                }
194
0
                continue;
195
0
            }
196
0
197
0
            // Add this to our list of valid targets
198
0
            GdkAtom atom = gdk_atom_intern(flavorStr.get(), FALSE);
199
0
            gtk_target_list_add(list, atom, 0, 0);
200
0
        }
201
0
    }
202
0
203
0
    // Get GTK clipboard (CLIPBOARD or PRIMARY)
204
0
    GtkClipboard *gtkClipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
205
0
206
0
    gint numTargets;
207
0
    GtkTargetEntry *gtkTargets = gtk_target_table_new_from_list(list, &numTargets);
208
0
209
0
    // Set getcallback and request to store data after an application exit
210
0
    if (gtkTargets &&
211
0
        gtk_clipboard_set_with_data(gtkClipboard, gtkTargets, numTargets,
212
0
                                    clipboard_get_cb, clipboard_clear_cb, this))
213
0
    {
214
0
        // We managed to set-up the clipboard so update internal state
215
0
        // We have to set it now because gtk_clipboard_set_with_data() calls clipboard_clear_cb()
216
0
        // which reset our internal state
217
0
        if (aWhichClipboard == kSelectionClipboard) {
218
0
            mSelectionOwner = aOwner;
219
0
            mSelectionTransferable = aTransferable;
220
0
        }
221
0
        else {
222
0
            mGlobalOwner = aOwner;
223
0
            mGlobalTransferable = aTransferable;
224
0
            gtk_clipboard_set_can_store(gtkClipboard, gtkTargets, numTargets);
225
0
        }
226
0
227
0
        rv = NS_OK;
228
0
    }
229
0
    else {
230
0
        rv = NS_ERROR_FAILURE;
231
0
    }
232
0
233
0
    gtk_target_table_free(gtkTargets, numTargets);
234
0
    gtk_target_list_unref(list);
235
0
236
0
    return rv;
237
0
}
238
239
void
240
nsClipboard::SetTransferableData(nsITransferable* aTransferable,
241
                                 nsCString&       aFlavor,
242
                                 const char*      aClipboardData,
243
                                 uint32_t         aClipboardDataLength)
244
0
{
245
0
  nsCOMPtr<nsISupports> wrapper;
246
0
  nsPrimitiveHelpers::CreatePrimitiveForData(aFlavor,
247
0
                                             aClipboardData,
248
0
                                             aClipboardDataLength,
249
0
                                             getter_AddRefs(wrapper));
250
0
  aTransferable->SetTransferData(aFlavor.get(),
251
0
                                 wrapper, aClipboardDataLength);
252
0
}
253
254
NS_IMETHODIMP
255
nsClipboard::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard)
256
0
{
257
0
    if (!aTransferable)
258
0
        return NS_ERROR_FAILURE;
259
0
260
0
    // Get a list of flavors this transferable can import
261
0
    nsCOMPtr<nsIArray> flavors;
262
0
    nsresult rv;
263
0
    rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavors));
264
0
    if (!flavors || NS_FAILED(rv))
265
0
        return NS_ERROR_FAILURE;
266
0
267
0
    uint32_t count;
268
0
    flavors->GetLength(&count);
269
0
    for (uint32_t i=0; i < count; i++) {
270
0
        nsCOMPtr<nsISupportsCString> currentFlavor;
271
0
        currentFlavor = do_QueryElementAt(flavors, i);
272
0
        if (!currentFlavor)
273
0
            continue;
274
0
275
0
        nsCString flavorStr;
276
0
        currentFlavor->ToString(getter_Copies(flavorStr));
277
0
278
0
        if (flavorStr.EqualsLiteral(kJPEGImageMime) ||
279
0
            flavorStr.EqualsLiteral(kJPGImageMime) ||
280
0
            flavorStr.EqualsLiteral(kPNGImageMime) ||
281
0
            flavorStr.EqualsLiteral(kGIFImageMime)) {
282
0
            // Emulate support for image/jpg
283
0
            if (flavorStr.EqualsLiteral(kJPGImageMime)) {
284
0
                flavorStr.Assign(kJPEGImageMime);
285
0
            }
286
0
287
0
            uint32_t    clipboardDataLength;
288
0
            const char* clipboardData =
289
0
                mContext->GetClipboardData(flavorStr.get(),
290
0
                                           aWhichClipboard,
291
0
                                           &clipboardDataLength);
292
0
            if (!clipboardData)
293
0
                continue;
294
0
295
0
            nsCOMPtr<nsIInputStream> byteStream;
296
0
            NS_NewByteInputStream(getter_AddRefs(byteStream),
297
0
                                  clipboardData,
298
0
                                  clipboardDataLength,
299
0
                                  NS_ASSIGNMENT_COPY);
300
0
            aTransferable->SetTransferData(flavorStr.get(), byteStream,
301
0
                                           sizeof(nsIInputStream*));
302
0
303
0
            mContext->ReleaseClipboardData(clipboardData);
304
0
            return NS_OK;
305
0
        }
306
0
307
0
        // Special case text/unicode since we can convert any
308
0
        // string into text/unicode
309
0
        if (flavorStr.EqualsLiteral(kUnicodeMime)) {
310
0
            const char* clipboardData =
311
0
                mContext->GetClipboardText(aWhichClipboard);
312
0
            if (!clipboardData) {
313
0
                // If the type was text/unicode and we couldn't get
314
0
                // text off the clipboard, run the next loop
315
0
                // iteration.
316
0
                continue;
317
0
            }
318
0
319
0
            // Convert utf-8 into our unicode format.
320
0
            NS_ConvertUTF8toUTF16 ucs2string(clipboardData);
321
0
            const char* unicodeData = (const char *)ToNewUnicode(ucs2string);
322
0
            uint32_t unicodeDataLength = ucs2string.Length() * 2;
323
0
            SetTransferableData(aTransferable, flavorStr,
324
0
                                unicodeData, unicodeDataLength);
325
0
            free((void *)unicodeData);
326
0
327
0
            mContext->ReleaseClipboardData(clipboardData);
328
0
            return NS_OK;
329
0
        }
330
0
331
0
332
0
        uint32_t clipboardDataLength;
333
0
        const char* clipboardData = mContext->GetClipboardData(
334
0
            flavorStr.get(), aWhichClipboard, &clipboardDataLength);
335
0
336
0
        if (clipboardData) {
337
0
            // Special case text/html since we can convert into UCS2
338
0
            if (flavorStr.EqualsLiteral(kHTMLMime)) {
339
0
                char16_t* htmlBody = nullptr;
340
0
                int32_t htmlBodyLen = 0;
341
0
                // Convert text/html into our unicode format
342
0
                ConvertHTMLtoUCS2(clipboardData, clipboardDataLength,
343
0
                                  &htmlBody, htmlBodyLen);
344
0
345
0
                // Try next data format?
346
0
                if (!htmlBodyLen) {
347
0
                    mContext->ReleaseClipboardData(clipboardData);
348
0
                    continue;
349
0
                }
350
0
351
0
                SetTransferableData(aTransferable, flavorStr,
352
0
                                    (const char*)htmlBody, htmlBodyLen * 2);
353
0
                free(htmlBody);
354
0
            } else {
355
0
                SetTransferableData(aTransferable, flavorStr,
356
0
                                    clipboardData, clipboardDataLength);
357
0
            }
358
0
359
0
            mContext->ReleaseClipboardData(clipboardData);
360
0
            return NS_OK;
361
0
        }
362
0
    }
363
0
364
0
    return NS_OK;
365
0
}
366
367
NS_IMETHODIMP
368
nsClipboard::EmptyClipboard(int32_t aWhichClipboard)
369
0
{
370
0
    if (aWhichClipboard == kSelectionClipboard) {
371
0
        if (mSelectionOwner) {
372
0
            mSelectionOwner->LosingOwnership(mSelectionTransferable);
373
0
            mSelectionOwner = nullptr;
374
0
        }
375
0
        mSelectionTransferable = nullptr;
376
0
    }
377
0
    else {
378
0
        if (mGlobalOwner) {
379
0
            mGlobalOwner->LosingOwnership(mGlobalTransferable);
380
0
            mGlobalOwner = nullptr;
381
0
        }
382
0
        mGlobalTransferable = nullptr;
383
0
    }
384
0
385
0
    return NS_OK;
386
0
}
387
388
NS_IMETHODIMP
389
nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, uint32_t aLength,
390
                                    int32_t aWhichClipboard, bool *_retval)
391
0
{
392
0
  if (!aFlavorList || !_retval)
393
0
      return NS_ERROR_NULL_POINTER;
394
0
395
0
  *_retval = false;
396
0
397
0
  int targetNums;
398
0
  GdkAtom* targets = mContext->GetTargets(aWhichClipboard, &targetNums);
399
0
  if (!targets)
400
0
      return NS_OK;
401
0
402
0
  // Walk through the provided types and try to match it to a
403
0
  // provided type.
404
0
  for (uint32_t i = 0; i < aLength && !*_retval; i++) {
405
0
      // We special case text/unicode here.
406
0
      if (!strcmp(aFlavorList[i], kUnicodeMime) &&
407
0
          gtk_targets_include_text(targets, targetNums)) {
408
0
          *_retval = true;
409
0
          break;
410
0
      }
411
0
412
0
      for (int32_t j = 0; j < targetNums; j++) {
413
0
          gchar *atom_name = gdk_atom_name(targets[j]);
414
0
          if (!atom_name)
415
0
              continue;
416
0
417
0
          if (!strcmp(atom_name, aFlavorList[i]))
418
0
              *_retval = true;
419
0
420
0
          // X clipboard supports image/jpeg, but we want to emulate support
421
0
          // for image/jpg as well
422
0
          if (!strcmp(aFlavorList[i], kJPGImageMime) &&
423
0
              !strcmp(atom_name, kJPEGImageMime)) {
424
0
              *_retval = true;
425
0
          }
426
0
427
0
          g_free(atom_name);
428
0
429
0
          if (*_retval)
430
0
              break;
431
0
      }
432
0
  }
433
0
434
0
  g_free(targets);
435
0
  return NS_OK;
436
0
}
437
438
NS_IMETHODIMP
439
nsClipboard::SupportsSelectionClipboard(bool *_retval)
440
0
{
441
0
    *_retval = mContext->HasSelectionSupport();
442
0
    return NS_OK;
443
0
}
444
445
NS_IMETHODIMP
446
nsClipboard::SupportsFindClipboard(bool* _retval)
447
0
{
448
0
  *_retval = false;
449
0
  return NS_OK;
450
0
}
451
452
nsITransferable *
453
nsClipboard::GetTransferable(int32_t aWhichClipboard)
454
0
{
455
0
    nsITransferable *retval;
456
0
457
0
    if (aWhichClipboard == kSelectionClipboard)
458
0
        retval = mSelectionTransferable.get();
459
0
    else
460
0
        retval = mGlobalTransferable.get();
461
0
462
0
    return retval;
463
0
}
464
465
void
466
nsClipboard::SelectionGetEvent(GtkClipboard     *aClipboard,
467
                               GtkSelectionData *aSelectionData)
468
0
{
469
0
    // Someone has asked us to hand them something.  The first thing
470
0
    // that we want to do is see if that something includes text.  If
471
0
    // it does, try to give it text/unicode after converting it to
472
0
    // utf-8.
473
0
474
0
    int32_t whichClipboard;
475
0
476
0
    // which clipboard?
477
0
    GdkAtom selection = gtk_selection_data_get_selection(aSelectionData);
478
0
    if (selection == GDK_SELECTION_PRIMARY)
479
0
        whichClipboard = kSelectionClipboard;
480
0
    else if (selection == GDK_SELECTION_CLIPBOARD)
481
0
        whichClipboard = kGlobalClipboard;
482
0
    else
483
0
        return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
484
0
485
0
    nsCOMPtr<nsITransferable> trans = GetTransferable(whichClipboard);
486
0
    if (!trans) {
487
0
      // We have nothing to serve
488
#ifdef DEBUG_CLIPBOARD
489
      printf("nsClipboard::SelectionGetEvent() - %s clipboard is empty!\n",
490
             whichClipboard == kSelectionClipboard ? "Selection" : "Global");
491
#endif
492
      return;
493
0
    }
494
0
495
0
    nsresult rv;
496
0
    nsCOMPtr<nsISupports> item;
497
0
    uint32_t len;
498
0
499
0
    GdkAtom selectionTarget = gtk_selection_data_get_target(aSelectionData);
500
0
501
0
    // Check to see if the selection data includes any of the string
502
0
    // types that we support.
503
0
    if (selectionTarget == gdk_atom_intern ("STRING", FALSE) ||
504
0
        selectionTarget == gdk_atom_intern ("TEXT", FALSE) ||
505
0
        selectionTarget == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
506
0
        selectionTarget == gdk_atom_intern ("UTF8_STRING", FALSE)) {
507
0
        // Try to convert our internal type into a text string.  Get
508
0
        // the transferable for this clipboard and try to get the
509
0
        // text/unicode type for it.
510
0
        rv = trans->GetTransferData("text/unicode", getter_AddRefs(item),
511
0
                                    &len);
512
0
        if (!item || NS_FAILED(rv))
513
0
            return;
514
0
515
0
        nsCOMPtr<nsISupportsString> wideString;
516
0
        wideString = do_QueryInterface(item);
517
0
        if (!wideString)
518
0
            return;
519
0
520
0
        nsAutoString ucs2string;
521
0
        wideString->GetData(ucs2string);
522
0
        char *utf8string = ToNewUTF8String(ucs2string);
523
0
        if (!utf8string)
524
0
            return;
525
0
526
0
        gtk_selection_data_set_text (aSelectionData, utf8string,
527
0
                                     strlen(utf8string));
528
0
529
0
        free(utf8string);
530
0
        return;
531
0
    }
532
0
533
0
    // Check to see if the selection data is an image type
534
0
    if (gtk_targets_include_image(&selectionTarget, 1, TRUE)) {
535
0
        // Look through our transfer data for the image
536
0
        static const char* const imageMimeTypes[] = {
537
0
            kNativeImageMime, kPNGImageMime, kJPEGImageMime, kJPGImageMime, kGIFImageMime };
538
0
        nsCOMPtr<nsISupports> imageItem;
539
0
        nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive;
540
0
        for (uint32_t i = 0; !ptrPrimitive && i < ArrayLength(imageMimeTypes); i++) {
541
0
            rv = trans->GetTransferData(imageMimeTypes[i], getter_AddRefs(imageItem), &len);
542
0
            ptrPrimitive = do_QueryInterface(imageItem);
543
0
        }
544
0
        if (!ptrPrimitive)
545
0
            return;
546
0
547
0
        nsCOMPtr<nsISupports> primitiveData;
548
0
        ptrPrimitive->GetData(getter_AddRefs(primitiveData));
549
0
        nsCOMPtr<imgIContainer> image(do_QueryInterface(primitiveData));
550
0
        if (!image) // Not getting an image for an image mime type!?
551
0
            return;
552
0
553
0
        GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(image);
554
0
        if (!pixbuf)
555
0
            return;
556
0
557
0
        gtk_selection_data_set_pixbuf(aSelectionData, pixbuf);
558
0
        g_object_unref(pixbuf);
559
0
        return;
560
0
    }
561
0
562
0
    // Try to match up the selection data target to something our
563
0
    // transferable provides.
564
0
    gchar *target_name = gdk_atom_name(selectionTarget);
565
0
    if (!target_name)
566
0
        return;
567
0
568
0
    rv = trans->GetTransferData(target_name, getter_AddRefs(item), &len);
569
0
    // nothing found?
570
0
    if (!item || NS_FAILED(rv)) {
571
0
        g_free(target_name);
572
0
        return;
573
0
    }
574
0
575
0
    void *primitive_data = nullptr;
576
0
    nsPrimitiveHelpers::CreateDataFromPrimitive(nsDependentCString(target_name),
577
0
                                                item, &primitive_data, len);
578
0
579
0
    if (primitive_data) {
580
0
        // Check to see if the selection data is text/html
581
0
        if (selectionTarget == gdk_atom_intern (kHTMLMime, FALSE)) {
582
0
            /*
583
0
             * "text/html" can be encoded UCS2. It is recommended that
584
0
             * documents transmitted as UCS2 always begin with a ZERO-WIDTH
585
0
             * NON-BREAKING SPACE character (hexadecimal FEFF, also called
586
0
             * Byte Order Mark (BOM)). Adding BOM can help other app to
587
0
             * detect mozilla use UCS2 encoding when copy-paste.
588
0
             */
589
0
            guchar *buffer = (guchar *)
590
0
                    g_malloc((len * sizeof(guchar)) + sizeof(char16_t));
591
0
            if (!buffer)
592
0
                return;
593
0
            char16_t prefix = 0xFEFF;
594
0
            memcpy(buffer, &prefix, sizeof(prefix));
595
0
            memcpy(buffer + sizeof(prefix), primitive_data, len);
596
0
            g_free((guchar *)primitive_data);
597
0
            primitive_data = (guchar *)buffer;
598
0
            len += sizeof(prefix);
599
0
        }
600
0
601
0
        gtk_selection_data_set(aSelectionData, selectionTarget,
602
0
                               8, /* 8 bits in a unit */
603
0
                               (const guchar *)primitive_data, len);
604
0
        g_free(primitive_data);
605
0
    }
606
0
607
0
    g_free(target_name);
608
0
609
0
}
610
611
void
612
nsClipboard::SelectionClearEvent(GtkClipboard *aGtkClipboard)
613
0
{
614
0
    int32_t whichClipboard;
615
0
616
0
    // which clipboard?
617
0
    if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_PRIMARY))
618
0
        whichClipboard = kSelectionClipboard;
619
0
    else if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_CLIPBOARD))
620
0
        whichClipboard = kGlobalClipboard;
621
0
    else
622
0
        return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
623
0
624
0
    EmptyClipboard(whichClipboard);
625
0
}
626
627
void
628
clipboard_get_cb(GtkClipboard *aGtkClipboard,
629
                 GtkSelectionData *aSelectionData,
630
                 guint info,
631
                 gpointer user_data)
632
0
{
633
0
    nsClipboard *aClipboard = static_cast<nsClipboard *>(user_data);
634
0
    aClipboard->SelectionGetEvent(aGtkClipboard, aSelectionData);
635
0
}
636
637
void
638
clipboard_clear_cb(GtkClipboard *aGtkClipboard,
639
                   gpointer user_data)
640
0
{
641
0
    nsClipboard *aClipboard = static_cast<nsClipboard *>(user_data);
642
0
    aClipboard->SelectionClearEvent(aGtkClipboard);
643
0
}
644
645
/*
646
 * when copy-paste, mozilla wants data encoded using UCS2,
647
 * other app such as StarOffice use "text/html"(RFC2854).
648
 * This function convert data(got from GTK clipboard)
649
 * to data mozilla wanted.
650
 *
651
 * data from GTK clipboard can be 3 forms:
652
 *  1. From current mozilla
653
 *     "text/html", charset = utf-16
654
 *  2. From old version mozilla or mozilla-based app
655
 *     content("body" only), charset = utf-16
656
 *  3. From other app who use "text/html" when copy-paste
657
 *     "text/html", has "charset" info
658
 *
659
 * data      : got from GTK clipboard
660
 * dataLength: got from GTK clipboard
661
 * body      : pass to Mozilla
662
 * bodyLength: pass to Mozilla
663
 */
664
void ConvertHTMLtoUCS2(const char* data, int32_t dataLength,
665
                       char16_t** unicodeData, int32_t& outUnicodeLen)
666
0
{
667
0
    nsAutoCString charset;
668
0
    GetHTMLCharset(data, dataLength, charset);// get charset of HTML
669
0
    if (charset.EqualsLiteral("UTF-16")) {//current mozilla
670
0
        outUnicodeLen = (dataLength / 2) - 1;
671
0
        *unicodeData =
672
0
            reinterpret_cast<char16_t*>
673
0
            (moz_xmalloc((outUnicodeLen + sizeof('\0')) * sizeof(char16_t)));
674
0
        memcpy(*unicodeData, data + sizeof(char16_t),
675
0
               outUnicodeLen * sizeof(char16_t));
676
0
        (*unicodeData)[outUnicodeLen] = '\0';
677
0
    } else if (charset.EqualsLiteral("UNKNOWN")) {
678
0
        outUnicodeLen = 0;
679
0
        return;
680
0
    } else {
681
0
        // app which use "text/html" to copy&paste
682
0
        // get the decoder
683
0
        auto encoding = Encoding::ForLabelNoReplacement(charset);
684
0
        if (!encoding) {
685
#ifdef DEBUG_CLIPBOARD
686
            g_print("        get unicode decoder error\n");
687
#endif
688
            outUnicodeLen = 0;
689
0
            return;
690
0
        }
691
0
        auto decoder = encoding->NewDecoder();
692
0
        CheckedInt<size_t> needed = decoder->MaxUTF16BufferLength(dataLength);
693
0
        if (!needed.isValid() || needed.value() > INT32_MAX) {
694
0
          outUnicodeLen = 0;
695
0
          return;
696
0
        }
697
0
698
0
        outUnicodeLen = 0;
699
0
        if (needed.value()) {
700
0
          *unicodeData = reinterpret_cast<char16_t*>(
701
0
            moz_xmalloc((needed.value() + 1) * sizeof(char16_t)));
702
0
          uint32_t result;
703
0
          size_t read;
704
0
          size_t written;
705
0
          bool hadErrors;
706
0
          Tie(result, read, written, hadErrors) =
707
0
            decoder->DecodeToUTF16(AsBytes(MakeSpan(data, dataLength)),
708
0
                                   MakeSpan(*unicodeData, needed.value()),
709
0
                                   true);
710
0
          MOZ_ASSERT(result == kInputEmpty);
711
0
          MOZ_ASSERT(read == size_t(dataLength));
712
0
          MOZ_ASSERT(written <= needed.value());
713
0
          Unused << hadErrors;
714
#ifdef DEBUG_CLIPBOARD
715
          if (read != dataLength)
716
            printf("didn't consume all the bytes\n");
717
#endif
718
          outUnicodeLen = written;
719
0
          // null terminate.
720
0
          (*unicodeData)[outUnicodeLen] = '\0';
721
0
        } // if valid length
722
0
    }
723
0
}
724
725
/*
726
 * get "charset" information from clipboard data
727
 * return value can be:
728
 *  1. "UTF-16":      mozilla or "text/html" with "charset=utf-16"
729
 *  2. "UNKNOWN":     mozilla can't detect what encode it use
730
 *  3. other:         "text/html" with other charset than utf-16
731
 */
732
void GetHTMLCharset(const char* data, int32_t dataLength, nsCString& str)
733
0
{
734
0
    // if detect "FFFE" or "FEFF", assume UTF-16
735
0
    char16_t* beginChar =  (char16_t*)data;
736
0
    if ((beginChar[0] == 0xFFFE) || (beginChar[0] == 0xFEFF)) {
737
0
        str.AssignLiteral("UTF-16");
738
0
        return;
739
0
    }
740
0
    // no "FFFE" and "FEFF", assume ASCII first to find "charset" info
741
0
    const nsDependentCString htmlStr(data, dataLength);
742
0
    nsACString::const_iterator start, end;
743
0
    htmlStr.BeginReading(start);
744
0
    htmlStr.EndReading(end);
745
0
    nsACString::const_iterator valueStart(start), valueEnd(start);
746
0
747
0
    if (CaseInsensitiveFindInReadable(
748
0
        NS_LITERAL_CSTRING("CONTENT=\"text/html;"),
749
0
        start, end)) {
750
0
        start = end;
751
0
        htmlStr.EndReading(end);
752
0
753
0
        if (CaseInsensitiveFindInReadable(
754
0
            NS_LITERAL_CSTRING("charset="),
755
0
            start, end)) {
756
0
            valueStart = end;
757
0
            start = end;
758
0
            htmlStr.EndReading(end);
759
0
760
0
            if (FindCharInReadable('"', start, end))
761
0
                valueEnd = start;
762
0
        }
763
0
    }
764
0
    // find "charset" in HTML
765
0
    if (valueStart != valueEnd) {
766
0
        str = Substring(valueStart, valueEnd);
767
0
        ToUpperCase(str);
768
#ifdef DEBUG_CLIPBOARD
769
        printf("Charset of HTML = %s\n", charsetUpperStr.get());
770
#endif
771
        return;
772
0
    }
773
0
    str.AssignLiteral("UNKNOWN");
774
0
}