1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
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/. */
8 #include "mozilla/ArrayUtils.h"
10 #include "nsArrayUtils.h"
11 #include "nsClipboard.h"
13 # include "nsClipboardX11.h"
15 #if defined(MOZ_WAYLAND)
16 # include "nsClipboardWayland.h"
17 # include "nsWaylandDisplay.h"
19 #include "nsGtkUtils.h"
22 #include "nsNetUtil.h"
23 #include "nsContentUtils.h"
24 #include "HeadlessClipboard.h"
25 #include "nsSupportsPrimitives.h"
27 #include "nsReadableUtils.h"
28 #include "nsPrimitiveHelpers.h"
29 #include "nsImageToPixbuf.h"
30 #include "nsStringStream.h"
31 #include "nsIFileURL.h"
32 #include "nsIObserverService.h"
33 #include "mozilla/Services.h"
34 #include "mozilla/RefPtr.h"
35 #include "mozilla/GRefPtr.h"
36 #include "mozilla/SchedulerGroup.h"
37 #include "mozilla/StaticPrefs_widget.h"
38 #include "mozilla/TimeStamp.h"
40 #include "WidgetUtilsGtk.h"
42 #include "imgIContainer.h"
46 # include <gtk/gtkx.h>
49 #include "mozilla/Encoding.h"
51 using namespace mozilla
;
53 // Idle timeout for receiving selection and property notify events (microsec)
54 // Right now it's set to 1 sec.
55 const int kClipboardTimeout
= 1000000;
57 // Defines how many event loop iterations will be done without sleep.
58 // We ususally get data in first 2-3 iterations unless some large object
59 // (an image for instance) is transferred through clipboard.
60 const int kClipboardFastIterationNum
= 3;
62 // We add this prefix to HTML markup, so that GetHTMLCharset can correctly
63 // detect the HTML as UTF-8 encoded.
64 static const char kHTMLMarkupPrefix
[] =
65 R
"(<meta http-equiv="content
-type
" content="text
/html
; charset
=utf
-8">)";
67 static const char kURIListMime
[] = "text/uri-list";
69 ClipboardTargets
nsRetrievalContext::sClipboardTargets
;
70 ClipboardTargets
nsRetrievalContext::sPrimaryTargets
;
72 // Callback when someone asks us for the data
73 static void clipboard_get_cb(GtkClipboard
* aGtkClipboard
,
74 GtkSelectionData
* aSelectionData
, guint info
,
77 // Callback when someone asks us to clear a clipboard
78 static void clipboard_clear_cb(GtkClipboard
* aGtkClipboard
, gpointer user_data
);
80 // Callback when owner of clipboard data is changed
81 static void clipboard_owner_change_cb(GtkClipboard
* aGtkClipboard
,
82 GdkEventOwnerChange
* aEvent
,
85 static bool GetHTMLCharset(Span
<const char> aData
, nsCString
& str
);
87 static void SetTransferableData(nsITransferable
* aTransferable
,
88 const nsACString
& aFlavor
,
89 const char* aClipboardData
,
90 uint32_t aClipboardDataLength
) {
91 LOGCLIP("SetTransferableData MIME %s\n", PromiseFlatCString(aFlavor
).get());
92 nsCOMPtr
<nsISupports
> wrapper
;
93 nsPrimitiveHelpers::CreatePrimitiveForData(
94 aFlavor
, aClipboardData
, aClipboardDataLength
, getter_AddRefs(wrapper
));
95 aTransferable
->SetTransferData(PromiseFlatCString(aFlavor
).get(), wrapper
);
98 ClipboardTargets
ClipboardTargets::Clone() {
103 reinterpret_cast<GdkAtom
*>(g_malloc(sizeof(GdkAtom
) * mCount
)));
104 memcpy(ret
.mTargets
.get(), mTargets
.get(), sizeof(GdkAtom
) * mCount
);
109 void ClipboardTargets::Set(ClipboardTargets aTargets
) {
110 mCount
= aTargets
.mCount
;
111 mTargets
= std::move(aTargets
.mTargets
);
114 void ClipboardData::SetData(Span
<const uint8_t> aData
) {
116 mLength
= aData
.Length();
118 mData
.reset(reinterpret_cast<char*>(g_malloc(sizeof(char) * mLength
)));
119 memcpy(mData
.get(), aData
.data(), sizeof(char) * mLength
);
123 void ClipboardData::SetText(Span
<const char> aData
) {
125 mLength
= aData
.Length();
128 reinterpret_cast<char*>(g_malloc(sizeof(char) * (mLength
+ 1))));
129 memcpy(mData
.get(), aData
.data(), sizeof(char) * mLength
);
130 mData
.get()[mLength
] = '\0';
134 void ClipboardData::SetTargets(ClipboardTargets aTargets
) {
135 mLength
= aTargets
.mCount
;
136 mData
.reset(reinterpret_cast<char*>(aTargets
.mTargets
.release()));
139 ClipboardTargets
ClipboardData::ExtractTargets() {
140 GUniquePtr
<GdkAtom
> targets(reinterpret_cast<GdkAtom
*>(mData
.release()));
141 uint32_t length
= std::exchange(mLength
, 0);
142 return ClipboardTargets
{std::move(targets
), length
};
145 GdkAtom
GetSelectionAtom(int32_t aWhichClipboard
) {
146 if (aWhichClipboard
== nsIClipboard::kGlobalClipboard
)
147 return GDK_SELECTION_CLIPBOARD
;
149 return GDK_SELECTION_PRIMARY
;
152 int GetGeckoClipboardType(GtkClipboard
* aGtkClipboard
) {
153 if (aGtkClipboard
== gtk_clipboard_get(GDK_SELECTION_PRIMARY
))
154 return nsClipboard::kSelectionClipboard
;
155 else if (aGtkClipboard
== gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
))
156 return nsClipboard::kGlobalClipboard
;
158 return -1; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
161 void nsRetrievalContext::ClearCachedTargetsClipboard(GtkClipboard
* aClipboard
,
164 LOGCLIP("nsRetrievalContext::ClearCachedTargetsClipboard()");
165 sClipboardTargets
.Clear();
168 void nsRetrievalContext::ClearCachedTargetsPrimary(GtkClipboard
* aClipboard
,
171 LOGCLIP("nsRetrievalContext::ClearCachedTargetsPrimary()");
172 sPrimaryTargets
.Clear();
175 ClipboardTargets
nsRetrievalContext::GetTargets(int32_t aWhichClipboard
) {
176 LOGCLIP("nsRetrievalContext::GetTargets(%s)\n",
177 aWhichClipboard
== nsClipboard::kSelectionClipboard
? "primary"
179 ClipboardTargets
& storedTargets
=
180 (aWhichClipboard
== nsClipboard::kSelectionClipboard
) ? sPrimaryTargets
182 if (!storedTargets
) {
183 LOGCLIP(" getting targets from system");
184 storedTargets
.Set(GetTargetsImpl(aWhichClipboard
));
186 LOGCLIP(" using cached targets");
188 return storedTargets
.Clone();
191 nsRetrievalContext::~nsRetrievalContext() {
192 sClipboardTargets
.Clear();
193 sPrimaryTargets
.Clear();
196 nsClipboard::nsClipboard()
197 : nsBaseClipboard(mozilla::dom::ClipboardCapabilities(
199 widget::GdkIsWaylandDisplay()
200 ? widget::WaylandDisplayGet()->IsPrimarySelectionEnabled()
203 true, /* supportsSelectionClipboard */
205 false /* supportsFindClipboard */,
206 false /* supportsSelectionCache */)) {
207 g_signal_connect(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
), "owner-change",
208 G_CALLBACK(clipboard_owner_change_cb
), this);
209 g_signal_connect(gtk_clipboard_get(GDK_SELECTION_PRIMARY
), "owner-change",
210 G_CALLBACK(clipboard_owner_change_cb
), this);
213 nsClipboard::~nsClipboard() {
214 g_signal_handlers_disconnect_by_func(
215 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
216 FuncToGpointer(clipboard_owner_change_cb
), this);
217 g_signal_handlers_disconnect_by_func(
218 gtk_clipboard_get(GDK_SELECTION_PRIMARY
),
219 FuncToGpointer(clipboard_owner_change_cb
), this);
222 NS_IMPL_ISUPPORTS_INHERITED(nsClipboard
, nsBaseClipboard
, nsIObserver
)
224 nsresult
nsClipboard::Init(void) {
226 if (widget::GdkIsX11Display()) {
227 mContext
= new nsRetrievalContextX11();
230 #if defined(MOZ_WAYLAND)
231 if (widget::GdkIsWaylandDisplay()) {
232 mContext
= new nsRetrievalContextWayland();
236 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
238 os
->AddObserver(this, "xpcom-shutdown", false);
245 nsClipboard::Observe(nsISupports
* aSubject
, const char* aTopic
,
246 const char16_t
* aData
) {
247 // Save global clipboard content to CLIPBOARD_MANAGER.
248 // gtk_clipboard_store() can run an event loop, so call from a dedicated
250 return SchedulerGroup::Dispatch(
251 NS_NewRunnableFunction("gtk_clipboard_store()", []() {
252 LOGCLIP("nsClipboard storing clipboard content\n");
253 gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
258 nsClipboard::SetNativeClipboardData(nsITransferable
* aTransferable
,
259 int32_t aWhichClipboard
) {
260 MOZ_DIAGNOSTIC_ASSERT(aTransferable
);
261 MOZ_DIAGNOSTIC_ASSERT(
262 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
264 // See if we can short cut
265 if ((aWhichClipboard
== kGlobalClipboard
&&
266 aTransferable
== mGlobalTransferable
.get()) ||
267 (aWhichClipboard
== kSelectionClipboard
&&
268 aTransferable
== mSelectionTransferable
.get())) {
272 LOGCLIP("nsClipboard::SetNativeClipboardData (%s)\n",
273 aWhichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
275 // List of suported targets
276 GtkTargetList
* list
= gtk_target_list_new(nullptr, 0);
278 // Get the types of supported flavors
279 nsTArray
<nsCString
> flavors
;
280 nsresult rv
= aTransferable
->FlavorsTransferableCanExport(flavors
);
282 LOGCLIP(" FlavorsTransferableCanExport failed!\n");
283 // Fall through. |gtkTargets| will be null below.
286 // Add all the flavors to this widget's supported type.
287 bool imagesAdded
= false;
288 for (uint32_t i
= 0; i
< flavors
.Length(); i
++) {
289 nsCString
& flavorStr
= flavors
[i
];
290 LOGCLIP(" processing target %s\n", flavorStr
.get());
292 // Special case text/plain since we can handle all of the string types.
293 if (flavorStr
.EqualsLiteral(kTextMime
)) {
294 LOGCLIP(" adding TEXT targets\n");
295 gtk_target_list_add_text_targets(list
, 0);
299 if (nsContentUtils::IsFlavorImage(flavorStr
)) {
300 // Don't bother adding image targets twice
302 // accept any writable image type
303 LOGCLIP(" adding IMAGE targets\n");
304 gtk_target_list_add_image_targets(list
, 0, TRUE
);
310 if (flavorStr
.EqualsLiteral(kFileMime
)) {
311 LOGCLIP(" adding text/uri-list target\n");
312 GdkAtom atom
= gdk_atom_intern(kURIListMime
, FALSE
);
313 gtk_target_list_add(list
, atom
, 0, 0);
317 // Add this to our list of valid targets
318 LOGCLIP(" adding OTHER target %s\n", flavorStr
.get());
319 GdkAtom atom
= gdk_atom_intern(flavorStr
.get(), FALSE
);
320 gtk_target_list_add(list
, atom
, 0, 0);
323 // Get GTK clipboard (CLIPBOARD or PRIMARY)
324 GtkClipboard
* gtkClipboard
=
325 gtk_clipboard_get(GetSelectionAtom(aWhichClipboard
));
328 GtkTargetEntry
* gtkTargets
=
329 gtk_target_table_new_from_list(list
, &numTargets
);
330 if (!gtkTargets
|| numTargets
== 0) {
332 " gtk_target_table_new_from_list() failed or empty list of "
334 // Clear references to the any old data and let GTK know that it is no
336 EmptyNativeClipboardData(aWhichClipboard
);
337 return NS_ERROR_FAILURE
;
340 ClearCachedTargets(aWhichClipboard
);
342 // Set getcallback and request to store data after an application exit
343 if (gtk_clipboard_set_with_data(gtkClipboard
, gtkTargets
, numTargets
,
344 clipboard_get_cb
, clipboard_clear_cb
, this)) {
345 // We managed to set-up the clipboard so update internal state
346 // We have to set it now because gtk_clipboard_set_with_data() calls
347 // clipboard_clear_cb() which reset our internal state
348 if (aWhichClipboard
== kSelectionClipboard
) {
349 mSelectionSequenceNumber
++;
350 mSelectionTransferable
= aTransferable
;
352 mGlobalSequenceNumber
++;
353 mGlobalTransferable
= aTransferable
;
354 gtk_clipboard_set_can_store(gtkClipboard
, gtkTargets
, numTargets
);
359 LOGCLIP(" gtk_clipboard_set_with_data() failed!\n");
360 EmptyNativeClipboardData(aWhichClipboard
);
361 rv
= NS_ERROR_FAILURE
;
364 gtk_target_table_free(gtkTargets
, numTargets
);
365 gtk_target_list_unref(list
);
370 mozilla::Result
<int32_t, nsresult
>
371 nsClipboard::GetNativeClipboardSequenceNumber(int32_t aWhichClipboard
) {
372 MOZ_DIAGNOSTIC_ASSERT(
373 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
374 return aWhichClipboard
== kSelectionClipboard
? mSelectionSequenceNumber
375 : mGlobalSequenceNumber
;
378 static bool IsMIMEAtFlavourList(const nsTArray
<nsCString
>& aFlavourList
,
380 for (const auto& flavorStr
: aFlavourList
) {
381 if (flavorStr
.Equals(aMime
)) {
388 // When clipboard contains only images, X11/Gtk tries to convert them
389 // to text when we request text instead of just fail to provide the data.
390 // So if clipboard contains images only remove text MIME offer.
391 bool nsClipboard::FilterImportedFlavors(int32_t aWhichClipboard
,
392 nsTArray
<nsCString
>& aFlavors
) {
393 LOGCLIP("nsClipboard::FilterImportedFlavors");
395 auto targets
= mContext
->GetTargets(aWhichClipboard
);
397 LOGCLIP(" X11: no targes at clipboard (null), quit.\n");
401 for (const auto& atom
: targets
.AsSpan()) {
402 GUniquePtr
<gchar
> atom_name(gdk_atom_name(atom
));
406 // Filter out system MIME types.
407 if (strcmp(atom_name
.get(), "TARGETS") == 0 ||
408 strcmp(atom_name
.get(), "TIMESTAMP") == 0 ||
409 strcmp(atom_name
.get(), "SAVE_TARGETS") == 0 ||
410 strcmp(atom_name
.get(), "MULTIPLE") == 0) {
413 // Filter out types which can't be converted to text.
414 if (strncmp(atom_name
.get(), "image/", 6) == 0 ||
415 strncmp(atom_name
.get(), "application/", 12) == 0 ||
416 strncmp(atom_name
.get(), "audio/", 6) == 0 ||
417 strncmp(atom_name
.get(), "video/", 6) == 0) {
420 // We have some other MIME type on clipboard which can be hopefully
421 // converted to text without any problem.
422 LOGCLIP(" X11: text types in clipboard, no need to filter them.\n");
426 // So make sure we offer only types we have at clipboard.
427 nsTArray
<nsCString
> clipboardFlavors
;
428 for (const auto& atom
: targets
.AsSpan()) {
429 GUniquePtr
<gchar
> atom_name(gdk_atom_name(atom
));
433 if (IsMIMEAtFlavourList(aFlavors
, atom_name
.get())) {
434 clipboardFlavors
.AppendElement(nsCString(atom_name
.get()));
437 aFlavors
.SwapElements(clipboardFlavors
);
439 LOGCLIP(" X11: Flavors which match clipboard content:\n");
440 for (uint32_t i
= 0; i
< aFlavors
.Length(); i
++) {
441 LOGCLIP(" %s\n", aFlavors
[i
].get());
447 static nsresult
GetTransferableFlavors(nsITransferable
* aTransferable
,
448 nsTArray
<nsCString
>& aFlavors
) {
449 if (!aTransferable
) {
450 return NS_ERROR_FAILURE
;
452 // Get a list of flavors this transferable can import
453 nsresult rv
= aTransferable
->FlavorsTransferableCanImport(aFlavors
);
455 LOGCLIP(" FlavorsTransferableCanImport falied!\n");
459 LOGCLIP(" Flavors which can be imported:");
460 for (const auto& flavor
: aFlavors
) {
461 LOGCLIP(" %s", flavor
.get());
467 static bool TransferableSetFile(nsITransferable
* aTransferable
,
468 const nsACString
& aURIList
) {
470 nsTArray
<nsCString
> uris
= mozilla::widget::ParseTextURIList(aURIList
);
471 if (!uris
.IsEmpty()) {
472 nsCOMPtr
<nsIURI
> fileURI
;
473 NS_NewURI(getter_AddRefs(fileURI
), uris
[0]);
474 if (nsCOMPtr
<nsIFileURL
> fileURL
= do_QueryInterface(fileURI
, &rv
)) {
475 nsCOMPtr
<nsIFile
> file
;
476 rv
= fileURL
->GetFile(getter_AddRefs(file
));
477 if (NS_SUCCEEDED(rv
)) {
478 aTransferable
->SetTransferData(kFileMime
, file
);
479 LOGCLIP(" successfully set file to clipboard\n");
487 static bool TransferableSetHTML(nsITransferable
* aTransferable
,
488 Span
<const char> aData
) {
489 nsLiteralCString
mimeType(kHTMLMime
);
491 // Convert text/html into our text format
492 nsAutoCString charset
;
493 if (!GetHTMLCharset(aData
, charset
)) {
494 // Fall back to utf-8 in case html/data is missing kHTMLMarkupPrefix.
495 LOGCLIP("Failed to get html/text encoding, fall back to utf-8.\n");
496 charset
.AssignLiteral("utf-8");
499 LOGCLIP("TransferableSetHTML: HTML detected charset %s", charset
.get());
500 // app which use "text/html" to copy&paste
502 auto encoding
= Encoding::ForLabelNoReplacement(charset
);
504 LOGCLIP("TransferableSetHTML: get unicode decoder error (charset: %s)",
509 // According to spec html UTF-16BE/LE should be switched to UTF-8
510 // https://html.spec.whatwg.org/#determining-the-character-encoding:utf-16-encoding-2
511 if (encoding
== UTF_16LE_ENCODING
|| encoding
== UTF_16BE_ENCODING
) {
512 encoding
= UTF_8_ENCODING
;
515 // Remove kHTMLMarkupPrefix again, it won't necessarily cause any
516 // issues, but might confuse other users.
517 const size_t prefixLen
= ArrayLength(kHTMLMarkupPrefix
) - 1;
518 if (aData
.Length() >= prefixLen
&& nsDependentCSubstring(aData
.To(prefixLen
))
519 .EqualsLiteral(kHTMLMarkupPrefix
)) {
520 aData
= aData
.From(prefixLen
);
523 nsAutoString unicodeData
;
524 auto [rv
, enc
] = encoding
->Decode(AsBytes(aData
), unicodeData
);
526 if (enc
!= UTF_8_ENCODING
&&
527 MOZ_LOG_TEST(gClipboardLog
, mozilla::LogLevel::Debug
)) {
528 nsCString decoderName
;
529 enc
->Name(decoderName
);
530 LOGCLIP("TransferableSetHTML: expected UTF-8 decoder but got %s",
535 LOGCLIP("TransferableSetHTML: failed to decode HTML");
538 SetTransferableData(aTransferable
, mimeType
,
539 (const char*)unicodeData
.BeginReading(),
540 unicodeData
.Length() * sizeof(char16_t
));
545 nsClipboard::GetNativeClipboardData(nsITransferable
* aTransferable
,
546 int32_t aWhichClipboard
) {
547 MOZ_DIAGNOSTIC_ASSERT(aTransferable
);
548 MOZ_DIAGNOSTIC_ASSERT(
549 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
551 LOGCLIP("nsClipboard::GetNativeClipboardData (%s)\n",
552 aWhichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
554 // TODO: Ensure we don't re-enter here.
556 return NS_ERROR_FAILURE
;
559 nsTArray
<nsCString
> flavors
;
560 nsresult rv
= GetTransferableFlavors(aTransferable
, flavors
);
561 NS_ENSURE_SUCCESS(rv
, rv
);
563 // Filter out MIME types on X11 to prevent unwanted conversions,
565 if (widget::GdkIsX11Display() &&
566 !FilterImportedFlavors(aWhichClipboard
, flavors
)) {
567 LOGCLIP(" Missing suitable clipboard data, quit.");
571 for (uint32_t i
= 0; i
< flavors
.Length(); i
++) {
572 nsCString
& flavorStr
= flavors
[i
];
574 if (flavorStr
.EqualsLiteral(kJPEGImageMime
) ||
575 flavorStr
.EqualsLiteral(kJPGImageMime
) ||
576 flavorStr
.EqualsLiteral(kPNGImageMime
) ||
577 flavorStr
.EqualsLiteral(kGIFImageMime
)) {
578 // Emulate support for image/jpg
579 if (flavorStr
.EqualsLiteral(kJPGImageMime
)) {
580 flavorStr
.Assign(kJPEGImageMime
);
583 LOGCLIP(" Getting image %s MIME clipboard data\n", flavorStr
.get());
586 mContext
->GetClipboardData(flavorStr
.get(), aWhichClipboard
);
587 if (!clipboardData
) {
588 LOGCLIP(" %s type is missing\n", flavorStr
.get());
592 nsCOMPtr
<nsIInputStream
> byteStream
;
593 NS_NewByteInputStream(getter_AddRefs(byteStream
), clipboardData
.AsSpan(),
595 aTransferable
->SetTransferData(flavorStr
.get(), byteStream
);
596 LOGCLIP(" got %s MIME data\n", flavorStr
.get());
600 // Special case text/plain since we can convert any
601 // string into text/plain
602 if (flavorStr
.EqualsLiteral(kTextMime
)) {
603 LOGCLIP(" Getting text %s MIME clipboard data\n", flavorStr
.get());
605 auto clipboardData
= mContext
->GetClipboardText(aWhichClipboard
);
606 if (!clipboardData
) {
607 LOGCLIP(" failed to get text data\n");
608 // If the type was text/plain and we couldn't get
609 // text off the clipboard, run the next loop
614 // Convert utf-8 into our text format.
615 NS_ConvertUTF8toUTF16
ucs2string(clipboardData
.get());
616 SetTransferableData(aTransferable
, flavorStr
,
617 (const char*)ucs2string
.BeginReading(),
618 ucs2string
.Length() * 2);
620 LOGCLIP(" got text data, length %zd\n", ucs2string
.Length());
624 if (flavorStr
.EqualsLiteral(kFileMime
)) {
625 LOGCLIP(" Getting %s file clipboard data\n", flavorStr
.get());
628 mContext
->GetClipboardData(kURIListMime
, aWhichClipboard
);
629 if (!clipboardData
) {
630 LOGCLIP(" text/uri-list type is missing\n");
634 nsDependentCSubstring
fileName(clipboardData
.AsSpan());
635 if (!TransferableSetFile(aTransferable
, fileName
)) {
641 LOGCLIP(" Getting %s MIME clipboard data\n", flavorStr
.get());
644 mContext
->GetClipboardData(flavorStr
.get(), aWhichClipboard
);
647 if (!clipboardData
) {
648 LOGCLIP(" %s type is missing\n", flavorStr
.get());
653 LOGCLIP(" got %s mime type data.\n", flavorStr
.get());
655 // Special case text/html since we can convert into UCS2
656 if (flavorStr
.EqualsLiteral(kHTMLMime
)) {
657 if (!TransferableSetHTML(aTransferable
, clipboardData
.AsSpan())) {
661 auto span
= clipboardData
.AsSpan();
662 SetTransferableData(aTransferable
, flavorStr
, span
.data(),
669 LOGCLIP(" failed to get clipboard content.\n");
680 struct DataCallbackHandler
{
681 RefPtr
<nsITransferable
> mTransferable
;
682 nsBaseClipboard::GetDataCallback mDataCallback
;
686 explicit DataCallbackHandler(RefPtr
<nsITransferable
> aTransferable
,
687 nsBaseClipboard::GetDataCallback
&& aDataCallback
,
688 const char* aMimeType
,
689 DataType aDataType
= DATATYPE_RAW
)
690 : mTransferable(std::move(aTransferable
)),
691 mDataCallback(std::move(aDataCallback
)),
692 mMimeType(aMimeType
),
693 mDataType(aDataType
) {
694 MOZ_COUNT_CTOR(DataCallbackHandler
);
695 LOGCLIP("DataCallbackHandler created [%p] MIME %s type %d", this,
696 mMimeType
.get(), mDataType
);
698 ~DataCallbackHandler() {
699 LOGCLIP("DataCallbackHandler deleted [%p]", this);
700 MOZ_COUNT_DTOR(DataCallbackHandler
);
704 static void AsyncGetTextImpl(nsITransferable
* aTransferable
,
705 int32_t aWhichClipboard
,
706 nsBaseClipboard::GetDataCallback
&& aCallback
) {
707 LOGCLIP("AsyncGetText() type '%s'",
708 aWhichClipboard
== nsClipboard::kSelectionClipboard
? "primary"
711 gtk_clipboard_request_text(
712 gtk_clipboard_get(GetSelectionAtom(aWhichClipboard
)),
713 [](GtkClipboard
* aClipboard
, const gchar
* aText
, gpointer aData
) -> void {
714 UniquePtr
<DataCallbackHandler
> ref(
715 static_cast<DataCallbackHandler
*>(aData
));
716 LOGCLIP("AsyncGetText async handler of [%p]", aData
);
718 size_t dataLength
= aText
? strlen(aText
) : 0;
719 if (dataLength
<= 0) {
720 LOGCLIP(" quit, text is not available");
721 ref
->mDataCallback(NS_OK
);
725 // Convert utf-8 into our unicode format.
726 NS_ConvertUTF8toUTF16
utf16string(aText
, dataLength
);
727 nsLiteralCString
flavor(kTextMime
);
728 SetTransferableData(ref
->mTransferable
, flavor
,
729 (const char*)utf16string
.BeginReading(),
730 utf16string
.Length() * 2);
731 LOGCLIP(" text is set, length = %d", (int)dataLength
);
732 ref
->mDataCallback(NS_OK
);
734 new DataCallbackHandler(aTransferable
, std::move(aCallback
), kTextMime
));
737 static void AsyncGetDataImpl(nsITransferable
* aTransferable
,
738 int32_t aWhichClipboard
, const char* aMimeType
,
740 nsBaseClipboard::GetDataCallback
&& aCallback
) {
741 LOGCLIP("AsyncGetData() type '%s'",
742 aWhichClipboard
== nsClipboard::kSelectionClipboard
? "primary"
745 const char* gtkMIMEType
= nullptr;
748 // Don't ask Gtk for application/x-moz-file
749 gtkMIMEType
= kURIListMime
;
754 gtkMIMEType
= aMimeType
;
758 gtk_clipboard_request_contents(
759 gtk_clipboard_get(GetSelectionAtom(aWhichClipboard
)),
760 gdk_atom_intern(gtkMIMEType
, FALSE
),
761 [](GtkClipboard
* aClipboard
, GtkSelectionData
* aSelection
,
762 gpointer aData
) -> void {
763 UniquePtr
<DataCallbackHandler
> ref(
764 static_cast<DataCallbackHandler
*>(aData
));
765 LOGCLIP("AsyncGetData async handler [%p] MIME %s type %d", aData
,
766 ref
->mMimeType
.get(), ref
->mDataType
);
768 int dataLength
= gtk_selection_data_get_length(aSelection
);
769 if (dataLength
<= 0) {
770 ref
->mDataCallback(NS_OK
);
773 const char* data
= (const char*)gtk_selection_data_get_data(aSelection
);
775 ref
->mDataCallback(NS_OK
);
778 switch (ref
->mDataType
) {
779 case DATATYPE_IMAGE
: {
780 LOGCLIP(" set image clipboard data");
781 nsCOMPtr
<nsIInputStream
> byteStream
;
782 NS_NewByteInputStream(getter_AddRefs(byteStream
),
783 Span(data
, dataLength
), NS_ASSIGNMENT_COPY
);
784 ref
->mTransferable
->SetTransferData(ref
->mMimeType
.get(),
788 case DATATYPE_FILE
: {
789 LOGCLIP(" set file clipboard data");
790 nsDependentCSubstring
file(data
, dataLength
);
791 TransferableSetFile(ref
->mTransferable
, file
);
794 case DATATYPE_HTML
: {
795 LOGCLIP(" html clipboard data");
796 Span
dataSpan(data
, dataLength
);
797 TransferableSetHTML(ref
->mTransferable
, dataSpan
);
801 LOGCLIP(" raw clipboard data %s", ref
->mMimeType
.get());
802 SetTransferableData(ref
->mTransferable
, ref
->mMimeType
, data
,
807 ref
->mDataCallback(NS_OK
);
809 new DataCallbackHandler(aTransferable
, std::move(aCallback
), aMimeType
,
813 static void AsyncGetDataFlavor(nsITransferable
* aTransferable
,
814 int32_t aWhichClipboard
, nsCString
& aFlavorStr
,
815 nsBaseClipboard::GetDataCallback
&& aCallback
) {
816 if (aFlavorStr
.EqualsLiteral(kJPEGImageMime
) ||
817 aFlavorStr
.EqualsLiteral(kJPGImageMime
) ||
818 aFlavorStr
.EqualsLiteral(kPNGImageMime
) ||
819 aFlavorStr
.EqualsLiteral(kGIFImageMime
)) {
820 // Emulate support for image/jpg
821 if (aFlavorStr
.EqualsLiteral(kJPGImageMime
)) {
822 aFlavorStr
.Assign(kJPEGImageMime
);
824 LOGCLIP(" Getting image %s MIME clipboard data", aFlavorStr
.get());
825 AsyncGetDataImpl(aTransferable
, aWhichClipboard
, aFlavorStr
.get(),
826 DATATYPE_IMAGE
, std::move(aCallback
));
829 // Special case text/plain since we can convert any
830 // string into text/plain
831 if (aFlavorStr
.EqualsLiteral(kTextMime
)) {
832 LOGCLIP(" Getting unicode clipboard data");
833 AsyncGetTextImpl(aTransferable
, aWhichClipboard
, std::move(aCallback
));
836 if (aFlavorStr
.EqualsLiteral(kFileMime
)) {
837 LOGCLIP(" Getting file clipboard data\n");
838 AsyncGetDataImpl(aTransferable
, aWhichClipboard
, aFlavorStr
.get(),
839 DATATYPE_FILE
, std::move(aCallback
));
842 if (aFlavorStr
.EqualsLiteral(kHTMLMime
)) {
843 LOGCLIP(" Getting HTML clipboard data");
844 AsyncGetDataImpl(aTransferable
, aWhichClipboard
, aFlavorStr
.get(),
845 DATATYPE_HTML
, std::move(aCallback
));
848 LOGCLIP(" Getting raw %s MIME clipboard data\n", aFlavorStr
.get());
849 AsyncGetDataImpl(aTransferable
, aWhichClipboard
, aFlavorStr
.get(),
850 DATATYPE_RAW
, std::move(aCallback
));
853 void nsClipboard::AsyncGetNativeClipboardData(nsITransferable
* aTransferable
,
854 int32_t aWhichClipboard
,
855 GetDataCallback
&& aCallback
) {
856 MOZ_DIAGNOSTIC_ASSERT(aTransferable
);
857 MOZ_DIAGNOSTIC_ASSERT(
858 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
860 LOGCLIP("nsClipboard::AsyncGetNativeClipboardData (%s)",
861 aWhichClipboard
== nsClipboard::kSelectionClipboard
? "primary"
863 nsTArray
<nsCString
> importedFlavors
;
864 nsresult rv
= GetTransferableFlavors(aTransferable
, importedFlavors
);
870 auto flavorsNum
= importedFlavors
.Length();
876 if (flavorsNum
> 1) {
877 LOGCLIP(" Only first MIME type (%s) will be imported from clipboard!",
878 importedFlavors
[0].get());
882 // Filter out MIME types on X11 to prevent unwanted conversions,
884 if (widget::GdkIsX11Display()) {
885 AsyncHasNativeClipboardDataMatchingFlavors(
886 importedFlavors
, aWhichClipboard
,
887 [aWhichClipboard
, transferable
= nsCOMPtr
{aTransferable
},
888 callback
= std::move(aCallback
)](auto aResultOrError
) mutable {
889 if (aResultOrError
.isErr()) {
890 callback(aResultOrError
.unwrapErr());
894 nsTArray
<nsCString
> clipboardFlavors
=
895 std::move(aResultOrError
.unwrap());
896 if (!clipboardFlavors
.Length()) {
897 LOGCLIP(" no flavors in clipboard, quit.");
902 AsyncGetDataFlavor(transferable
, aWhichClipboard
, clipboardFlavors
[0],
903 std::move(callback
));
908 // Read clipboard directly on Wayland
909 AsyncGetDataFlavor(aTransferable
, aWhichClipboard
, importedFlavors
[0],
910 std::move(aCallback
));
913 nsresult
nsClipboard::EmptyNativeClipboardData(int32_t aWhichClipboard
) {
914 MOZ_DIAGNOSTIC_ASSERT(
915 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
917 LOGCLIP("nsClipboard::EmptyNativeClipboardData (%s)\n",
918 aWhichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
919 if (aWhichClipboard
== kSelectionClipboard
) {
920 if (mSelectionTransferable
) {
921 gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_PRIMARY
));
922 MOZ_ASSERT(!mSelectionTransferable
);
925 if (mGlobalTransferable
) {
926 gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
927 MOZ_ASSERT(!mGlobalTransferable
);
930 ClearCachedTargets(aWhichClipboard
);
934 void nsClipboard::ClearTransferable(int32_t aWhichClipboard
) {
935 if (aWhichClipboard
== kSelectionClipboard
) {
936 mSelectionSequenceNumber
++;
937 mSelectionTransferable
= nullptr;
939 mGlobalSequenceNumber
++;
940 mGlobalTransferable
= nullptr;
944 static bool FlavorMatchesTarget(const nsACString
& aFlavor
, GdkAtom aTarget
) {
945 GUniquePtr
<gchar
> atom_name(gdk_atom_name(aTarget
));
949 if (aFlavor
.Equals(atom_name
.get())) {
950 LOGCLIP(" has %s\n", atom_name
.get());
953 // X clipboard supports image/jpeg, but we want to emulate support
954 // for image/jpg as well
955 if (aFlavor
.EqualsLiteral(kJPGImageMime
) &&
956 !strcmp(atom_name
.get(), kJPEGImageMime
)) {
957 LOGCLIP(" has image/jpg\n");
960 // application/x-moz-file should be treated like text/uri-list
961 if (aFlavor
.EqualsLiteral(kFileMime
) &&
962 !strcmp(atom_name
.get(), kURIListMime
)) {
963 LOGCLIP(" has text/uri-list treating as application/x-moz-file");
969 mozilla::Result
<bool, nsresult
>
970 nsClipboard::HasNativeClipboardDataMatchingFlavors(
971 const nsTArray
<nsCString
>& aFlavorList
, int32_t aWhichClipboard
) {
972 MOZ_DIAGNOSTIC_ASSERT(
973 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
975 LOGCLIP("nsClipboard::HasNativeClipboardDataMatchingFlavors (%s)\n",
976 aWhichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
979 return Err(NS_ERROR_FAILURE
);
982 auto targets
= mContext
->GetTargets(aWhichClipboard
);
984 LOGCLIP(" no targes at clipboard (null)\n");
989 if (LOGCLIP_ENABLED()) {
990 LOGCLIP(" Asking for content:\n");
991 for (auto& flavor
: aFlavorList
) {
992 LOGCLIP(" MIME %s\n", flavor
.get());
994 LOGCLIP(" Clipboard content (target nums %zu):\n",
995 targets
.AsSpan().Length());
996 for (const auto& target
: targets
.AsSpan()) {
997 GUniquePtr
<gchar
> atom_name(gdk_atom_name(target
));
999 LOGCLIP(" failed to get MIME\n");
1002 LOGCLIP(" MIME %s\n", atom_name
.get());
1007 // Walk through the provided types and try to match it to a
1009 for (auto& flavor
: aFlavorList
) {
1010 // We special case text/plain here.
1011 if (flavor
.EqualsLiteral(kTextMime
) &&
1012 gtk_targets_include_text(targets
.AsSpan().data(),
1013 targets
.AsSpan().Length())) {
1014 LOGCLIP(" has kTextMime\n");
1017 for (const auto& target
: targets
.AsSpan()) {
1018 if (FlavorMatchesTarget(flavor
, target
)) {
1024 LOGCLIP(" no targes at clipboard (bad match)\n");
1028 struct TragetCallbackHandler
{
1029 TragetCallbackHandler(const nsTArray
<nsCString
>& aAcceptedFlavorList
,
1030 nsBaseClipboard::HasMatchingFlavorsCallback
&& aCallback
)
1031 : mAcceptedFlavorList(aAcceptedFlavorList
.Clone()),
1032 mCallback(std::move(aCallback
)) {
1033 LOGCLIP("TragetCallbackHandler(%p) created", this);
1035 ~TragetCallbackHandler() {
1036 LOGCLIP("TragetCallbackHandler(%p) deleted", this);
1038 nsTArray
<nsCString
> mAcceptedFlavorList
;
1039 nsBaseClipboard::HasMatchingFlavorsCallback mCallback
;
1042 void nsClipboard::AsyncHasNativeClipboardDataMatchingFlavors(
1043 const nsTArray
<nsCString
>& aFlavorList
, int32_t aWhichClipboard
,
1044 nsBaseClipboard::HasMatchingFlavorsCallback
&& aCallback
) {
1045 MOZ_DIAGNOSTIC_ASSERT(
1046 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
));
1048 LOGCLIP("nsClipboard::AsyncHasNativeClipboardDataMatchingFlavors (%s)",
1049 aWhichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
1051 gtk_clipboard_request_contents(
1052 gtk_clipboard_get(GetSelectionAtom(aWhichClipboard
)),
1053 gdk_atom_intern("TARGETS", FALSE
),
1054 [](GtkClipboard
* aClipboard
, GtkSelectionData
* aSelection
,
1055 gpointer aData
) -> void {
1056 LOGCLIP("gtk_clipboard_request_contents async handler (%p)", aData
);
1057 UniquePtr
<TragetCallbackHandler
> handler(
1058 static_cast<TragetCallbackHandler
*>(aData
));
1060 GdkAtom
* targets
= nullptr;
1061 gint targetsNum
= 0;
1062 if (gtk_selection_data_get_length(aSelection
) > 0) {
1063 gtk_selection_data_get_targets(aSelection
, &targets
, &targetsNum
);
1065 nsTArray
<nsCString
> results
;
1067 for (auto& flavor
: handler
->mAcceptedFlavorList
) {
1068 LOGCLIP(" looking for %s", flavor
.get());
1069 if (flavor
.EqualsLiteral(kTextMime
) &&
1070 gtk_targets_include_text(targets
, targetsNum
)) {
1071 results
.AppendElement(flavor
);
1072 LOGCLIP(" has kTextMime\n");
1075 for (int i
= 0; i
< targetsNum
; i
++) {
1076 if (FlavorMatchesTarget(flavor
, targets
[i
])) {
1077 results
.AppendElement(flavor
);
1082 handler
->mCallback(std::move(results
));
1084 new TragetCallbackHandler(aFlavorList
, std::move(aCallback
)));
1087 nsITransferable
* nsClipboard::GetTransferable(int32_t aWhichClipboard
) {
1088 nsITransferable
* retval
;
1090 if (aWhichClipboard
== kSelectionClipboard
)
1091 retval
= mSelectionTransferable
.get();
1093 retval
= mGlobalTransferable
.get();
1098 void nsClipboard::SelectionGetEvent(GtkClipboard
* aClipboard
,
1099 GtkSelectionData
* aSelectionData
) {
1100 // Someone has asked us to hand them something. The first thing
1101 // that we want to do is see if that something includes text. If
1102 // it does, try to give it text/plain after converting it to
1105 int32_t whichClipboard
;
1108 GdkAtom selection
= gtk_selection_data_get_selection(aSelectionData
);
1109 if (selection
== GDK_SELECTION_PRIMARY
)
1110 whichClipboard
= kSelectionClipboard
;
1111 else if (selection
== GDK_SELECTION_CLIPBOARD
)
1112 whichClipboard
= kGlobalClipboard
;
1114 return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
1116 LOGCLIP("nsClipboard::SelectionGetEvent (%s)\n",
1117 whichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
1119 nsCOMPtr
<nsITransferable
> trans
= GetTransferable(whichClipboard
);
1121 // We have nothing to serve
1122 LOGCLIP("nsClipboard::SelectionGetEvent() - %s clipboard is empty!\n",
1123 whichClipboard
== kSelectionClipboard
? "Primary" : "Clipboard");
1128 nsCOMPtr
<nsISupports
> item
;
1130 GdkAtom selectionTarget
= gtk_selection_data_get_target(aSelectionData
);
1131 LOGCLIP(" selection target %s\n",
1132 GUniquePtr
<gchar
>(gdk_atom_name(selectionTarget
)).get());
1134 // Check to see if the selection data is some text type.
1135 if (gtk_targets_include_text(&selectionTarget
, 1)) {
1136 LOGCLIP(" providing text/plain data\n");
1137 // Try to convert our internal type into a text string. Get
1138 // the transferable for this clipboard and try to get the
1139 // text/plain type for it.
1140 rv
= trans
->GetTransferData("text/plain", getter_AddRefs(item
));
1141 if (NS_FAILED(rv
) || !item
) {
1142 LOGCLIP(" GetTransferData() failed to get text/plain!\n");
1146 nsCOMPtr
<nsISupportsString
> wideString
;
1147 wideString
= do_QueryInterface(item
);
1148 if (!wideString
) return;
1150 nsAutoString ucs2string
;
1151 wideString
->GetData(ucs2string
);
1152 NS_ConvertUTF16toUTF8
utf8string(ucs2string
);
1154 LOGCLIP(" sent %zd bytes of utf-8 data\n", utf8string
.Length());
1155 if (selectionTarget
== gdk_atom_intern("text/plain;charset=utf-8", FALSE
)) {
1157 " using gtk_selection_data_set for 'text/plain;charset=utf-8'\n");
1158 // Bypass gtk_selection_data_set_text, which will convert \n to \r\n
1159 // in some versions of GTK.
1160 gtk_selection_data_set(aSelectionData
, selectionTarget
, 8,
1161 reinterpret_cast<const guchar
*>(utf8string
.get()),
1162 utf8string
.Length());
1164 gtk_selection_data_set_text(aSelectionData
, utf8string
.get(),
1165 utf8string
.Length());
1170 // Check to see if the selection data is an image type
1171 if (gtk_targets_include_image(&selectionTarget
, 1, TRUE
)) {
1172 LOGCLIP(" providing image data\n");
1173 // Look through our transfer data for the image
1174 static const char* const imageMimeTypes
[] = {kNativeImageMime
,
1175 kPNGImageMime
, kJPEGImageMime
,
1176 kJPGImageMime
, kGIFImageMime
};
1177 nsCOMPtr
<nsISupports
> imageItem
;
1178 nsCOMPtr
<imgIContainer
> image
;
1179 for (uint32_t i
= 0; i
< ArrayLength(imageMimeTypes
); i
++) {
1180 rv
= trans
->GetTransferData(imageMimeTypes
[i
], getter_AddRefs(imageItem
));
1181 if (NS_FAILED(rv
)) {
1182 LOGCLIP(" %s is missing at GetTransferData()\n", imageMimeTypes
[i
]);
1186 image
= do_QueryInterface(imageItem
);
1188 LOGCLIP(" %s is available at GetTransferData()\n",
1194 if (!image
) { // Not getting an image for an image mime type!?
1195 LOGCLIP(" Failed to get any image mime from GetTransferData()!\n");
1199 RefPtr
<GdkPixbuf
> pixbuf
= nsImageToPixbuf::ImageToPixbuf(image
);
1201 LOGCLIP(" nsImageToPixbuf::ImageToPixbuf() failed!\n");
1205 LOGCLIP(" Setting pixbuf image data as %s\n",
1206 GUniquePtr
<gchar
>(gdk_atom_name(selectionTarget
)).get());
1207 gtk_selection_data_set_pixbuf(aSelectionData
, pixbuf
);
1211 if (selectionTarget
== gdk_atom_intern(kHTMLMime
, FALSE
)) {
1212 LOGCLIP(" providing %s data\n", kHTMLMime
);
1213 rv
= trans
->GetTransferData(kHTMLMime
, getter_AddRefs(item
));
1214 if (NS_FAILED(rv
) || !item
) {
1215 LOGCLIP(" failed to get %s data by GetTransferData()!\n", kHTMLMime
);
1219 nsCOMPtr
<nsISupportsString
> wideString
;
1220 wideString
= do_QueryInterface(item
);
1222 LOGCLIP(" failed to get wideString interface!");
1226 nsAutoString ucs2string
;
1227 wideString
->GetData(ucs2string
);
1230 // Add the prefix so the encoding is correctly detected.
1231 html
.AppendLiteral(kHTMLMarkupPrefix
);
1232 AppendUTF16toUTF8(ucs2string
, html
);
1234 LOGCLIP(" Setting %zd bytes of %s data\n", html
.Length(),
1235 GUniquePtr
<gchar
>(gdk_atom_name(selectionTarget
)).get());
1236 gtk_selection_data_set(aSelectionData
, selectionTarget
, 8,
1237 (const guchar
*)html
.get(), html
.Length());
1241 // We put kFileMime onto the clipboard as kURIListMime.
1242 if (selectionTarget
== gdk_atom_intern(kURIListMime
, FALSE
)) {
1243 LOGCLIP(" providing %s data\n", kURIListMime
);
1244 rv
= trans
->GetTransferData(kFileMime
, getter_AddRefs(item
));
1245 if (NS_FAILED(rv
) || !item
) {
1246 LOGCLIP(" failed to get %s data by GetTransferData()!\n", kFileMime
);
1250 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(item
);
1252 LOGCLIP(" failed to get nsIFile interface!");
1256 nsCOMPtr
<nsIURI
> fileURI
;
1257 rv
= NS_NewFileURI(getter_AddRefs(fileURI
), file
);
1258 if (NS_FAILED(rv
)) {
1259 LOGCLIP(" failed to get fileURI\n");
1264 if (NS_FAILED(fileURI
->GetSpec(uri
))) {
1265 LOGCLIP(" failed to get fileURI spec\n");
1269 LOGCLIP(" Setting %zd bytes of data\n", uri
.Length());
1270 gtk_selection_data_set(aSelectionData
, selectionTarget
, 8,
1271 (const guchar
*)uri
.get(), uri
.Length());
1275 LOGCLIP(" Try if we have anything at GetTransferData() for %s\n",
1276 GUniquePtr
<gchar
>(gdk_atom_name(selectionTarget
)).get());
1278 // Try to match up the selection data target to something our
1279 // transferable provides.
1280 GUniquePtr
<gchar
> target_name(gdk_atom_name(selectionTarget
));
1282 LOGCLIP(" Failed to get target name!\n");
1286 rv
= trans
->GetTransferData(target_name
.get(), getter_AddRefs(item
));
1288 if (NS_FAILED(rv
) || !item
) {
1289 LOGCLIP(" Failed to get anything from GetTransferData()!\n");
1293 void* primitive_data
= nullptr;
1294 uint32_t dataLen
= 0;
1295 nsPrimitiveHelpers::CreateDataFromPrimitive(
1296 nsDependentCString(target_name
.get()), item
, &primitive_data
, &dataLen
);
1297 if (!primitive_data
) {
1298 LOGCLIP(" Failed to get primitive data!\n");
1302 LOGCLIP(" Setting %s as a primitive data type, %d bytes\n",
1303 target_name
.get(), dataLen
);
1304 gtk_selection_data_set(aSelectionData
, selectionTarget
,
1305 8, /* 8 bits in a unit */
1306 (const guchar
*)primitive_data
, dataLen
);
1307 free(primitive_data
);
1310 void nsClipboard::ClearCachedTargets(int32_t aWhichClipboard
) {
1311 if (aWhichClipboard
== kSelectionClipboard
) {
1312 nsRetrievalContext::ClearCachedTargetsPrimary(nullptr, nullptr, nullptr);
1314 nsRetrievalContext::ClearCachedTargetsClipboard(nullptr, nullptr, nullptr);
1318 void nsClipboard::SelectionClearEvent(GtkClipboard
* aGtkClipboard
) {
1319 int32_t whichClipboard
= GetGeckoClipboardType(aGtkClipboard
);
1320 if (whichClipboard
< 0) {
1323 LOGCLIP("nsClipboard::SelectionClearEvent (%s)\n",
1324 whichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
1325 ClearCachedTargets(whichClipboard
);
1326 ClearTransferable(whichClipboard
);
1327 ClearClipboardCache(whichClipboard
);
1330 void nsClipboard::OwnerChangedEvent(GtkClipboard
* aGtkClipboard
,
1331 GdkEventOwnerChange
* aEvent
) {
1332 int32_t whichClipboard
= GetGeckoClipboardType(aGtkClipboard
);
1333 if (whichClipboard
< 0) {
1336 LOGCLIP("nsClipboard::OwnerChangedEvent (%s)\n",
1337 whichClipboard
== kSelectionClipboard
? "primary" : "clipboard");
1338 GtkWidget
* gtkWidget
= [aEvent
]() -> GtkWidget
* {
1339 if (!aEvent
->owner
) {
1342 gpointer user_data
= nullptr;
1343 gdk_window_get_user_data(aEvent
->owner
, &user_data
);
1344 return GTK_WIDGET(user_data
);
1346 // If we can get GtkWidget from the current clipboard owner, this
1347 // owner-changed event must be triggered by ourself via calling
1348 // gtk_clipboard_set_with_data, the sequence number should already be handled.
1350 if (whichClipboard
== kSelectionClipboard
) {
1351 mSelectionSequenceNumber
++;
1353 mGlobalSequenceNumber
++;
1357 ClearCachedTargets(whichClipboard
);
1360 void clipboard_get_cb(GtkClipboard
* aGtkClipboard
,
1361 GtkSelectionData
* aSelectionData
, guint info
,
1362 gpointer user_data
) {
1363 LOGCLIP("clipboard_get_cb() callback\n");
1364 nsClipboard
* clipboard
= static_cast<nsClipboard
*>(user_data
);
1365 clipboard
->SelectionGetEvent(aGtkClipboard
, aSelectionData
);
1368 void clipboard_clear_cb(GtkClipboard
* aGtkClipboard
, gpointer user_data
) {
1369 LOGCLIP("clipboard_clear_cb() callback\n");
1370 nsClipboard
* clipboard
= static_cast<nsClipboard
*>(user_data
);
1371 clipboard
->SelectionClearEvent(aGtkClipboard
);
1374 void clipboard_owner_change_cb(GtkClipboard
* aGtkClipboard
,
1375 GdkEventOwnerChange
* aEvent
,
1376 gpointer aUserData
) {
1377 LOGCLIP("clipboard_owner_change_cb() callback\n");
1378 nsClipboard
* clipboard
= static_cast<nsClipboard
*>(aUserData
);
1379 clipboard
->OwnerChangedEvent(aGtkClipboard
, aEvent
);
1383 * This function extracts the encoding label from the subset of HTML internal
1384 * encoding declaration syntax that uses the old long form with double quotes
1385 * and without spaces around the equals sign between the "content" attribute
1386 * name and the attribute value.
1388 * This was added for the sake of an ancient version of StarOffice
1389 * in the pre-UTF-8 era in bug 123389. It is unclear if supporting
1390 * non-UTF-8 encodings is still necessary and if this function
1391 * still needs to exist.
1393 * As of December 2022, both Gecko and LibreOffice emit an UTF-8
1394 * declaration that this function successfully extracts "UTF-8" from,
1395 * but that's also the default that we fall back on if this function
1396 * fails to extract a label.
1398 bool GetHTMLCharset(Span
<const char> aData
, nsCString
& aFoundCharset
) {
1399 // Assume ASCII first to find "charset" info
1400 const nsDependentCSubstring
htmlStr(aData
);
1401 nsACString::const_iterator start
, end
;
1402 htmlStr
.BeginReading(start
);
1403 htmlStr
.EndReading(end
);
1404 nsACString::const_iterator
valueStart(start
), valueEnd(start
);
1406 if (CaseInsensitiveFindInReadable("CONTENT=\"text/html;"_ns
, start
, end
)) {
1408 htmlStr
.EndReading(end
);
1410 if (CaseInsensitiveFindInReadable("charset="_ns
, start
, end
)) {
1413 htmlStr
.EndReading(end
);
1415 if (FindCharInReadable('"', start
, end
)) valueEnd
= start
;
1418 // find "charset" in HTML
1419 if (valueStart
!= valueEnd
) {
1420 aFoundCharset
= Substring(valueStart
, valueEnd
);
1421 ToUpperCase(aFoundCharset
);