gecko-dev/widget/gtk/nsClipboard.cpp
Brindusan Cristian 817b88aae9 Backed out 4 changesets (bug 1590965) as requested by karlt on irc. CLOSED TREE
Backed out changeset f68c212c8b2a (bug 1590965)
Backed out changeset 1ced33f66353 (bug 1590965)
Backed out changeset 22f28d146e38 (bug 1590965)
Backed out changeset 70331e97107e (bug 1590965)
2019-11-06 02:32:29 +02:00

760 lines
24 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=4:tabstop=4:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ArrayUtils.h"
#include "nsArrayUtils.h"
#include "nsClipboard.h"
#include "nsClipboardX11.h"
#if defined(MOZ_WAYLAND)
# include "nsClipboardWayland.h"
#endif
#include "nsContentUtils.h"
#include "HeadlessClipboard.h"
#include "nsSupportsPrimitives.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsPrimitiveHelpers.h"
#include "nsIServiceManager.h"
#include "nsImageToPixbuf.h"
#include "nsStringStream.h"
#include "nsIObserverService.h"
#include "mozilla/Services.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
#include "imgIContainer.h"
#include <gtk/gtk.h>
#include <gtk/gtkx.h>
#include "mozilla/Encoding.h"
using namespace mozilla;
// Idle timeout for receiving selection and property notify events (microsec)
const int kClipboardTimeout = 500000;
// We add this prefix to HTML markup, so that GetHTMLCharset can correctly
// detect the HTML as UTF-8 encoded.
static const char kHTMLMarkupPrefix[] =
R"(<meta http-equiv="content-type" content="text/html; charset=utf-8">)";
// Callback when someone asks us for the data
void clipboard_get_cb(GtkClipboard* aGtkClipboard,
GtkSelectionData* aSelectionData, guint info,
gpointer user_data);
// Callback when someone asks us to clear a clipboard
void clipboard_clear_cb(GtkClipboard* aGtkClipboard, gpointer user_data);
static void ConvertHTMLtoUCS2(const char* data, int32_t dataLength,
char16_t** unicodeData, int32_t& outUnicodeLen);
static void GetHTMLCharset(const char* data, int32_t dataLength,
nsCString& str);
GdkAtom GetSelectionAtom(int32_t aWhichClipboard) {
if (aWhichClipboard == nsIClipboard::kGlobalClipboard)
return GDK_SELECTION_CLIPBOARD;
return GDK_SELECTION_PRIMARY;
}
int GetGeckoClipboardType(GtkClipboard* aGtkClipboard) {
if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_PRIMARY))
return nsClipboard::kSelectionClipboard;
else if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_CLIPBOARD))
return nsClipboard::kGlobalClipboard;
else
return -1; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
}
nsClipboard::nsClipboard() {}
nsClipboard::~nsClipboard() {
// We have to clear clipboard before gdk_display_close() call.
// See bug 531580 for details.
if (mGlobalTransferable) {
gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
}
if (mSelectionTransferable) {
gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
}
}
NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard, nsIObserver)
nsresult nsClipboard::Init(void) {
GdkDisplay* display = gdk_display_get_default();
// Create a nsRetrievalContext. If there's no default display
// create the X11 one as a fallback.
if (!display || GDK_IS_X11_DISPLAY(display)) {
mContext = new nsRetrievalContextX11();
#if defined(MOZ_WAYLAND)
} else {
mContext = new nsRetrievalContextWayland();
#endif
}
NS_ASSERTION(mContext, "Missing nsRetrievalContext for nsClipboard!");
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
os->AddObserver(this, "quit-application", false);
os->AddObserver(this, "xpcom-shutdown", false);
}
return NS_OK;
}
nsresult nsClipboard::Store(void) {
LOGCLIP(("nsClipboard::Store\n"));
if (mGlobalTransferable) {
GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
gtk_clipboard_store(clipboard);
}
return NS_OK;
}
NS_IMETHODIMP
nsClipboard::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
Store();
return NS_OK;
}
NS_IMETHODIMP
nsClipboard::SetData(nsITransferable* aTransferable, nsIClipboardOwner* aOwner,
int32_t aWhichClipboard) {
// See if we can short cut
if ((aWhichClipboard == kGlobalClipboard &&
aTransferable == mGlobalTransferable.get() &&
aOwner == mGlobalOwner.get()) ||
(aWhichClipboard == kSelectionClipboard &&
aTransferable == mSelectionTransferable.get() &&
aOwner == mSelectionOwner.get())) {
return NS_OK;
}
LOGCLIP(("nsClipboard::SetData (%s)\n",
aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"));
// Clear out the clipboard in order to set the new data
EmptyClipboard(aWhichClipboard);
// List of suported targets
GtkTargetList* list = gtk_target_list_new(nullptr, 0);
// Get the types of supported flavors
nsTArray<nsCString> flavors;
nsresult rv = aTransferable->FlavorsTransferableCanExport(flavors);
if (NS_FAILED(rv)) {
LOGCLIP((" FlavorsTransferableCanExport failed!\n"));
return rv;
}
// Add all the flavors to this widget's supported type.
bool imagesAdded = false;
for (uint32_t i = 0; i < flavors.Length(); i++) {
nsCString& flavorStr = flavors[i];
// Special case text/unicode since we can handle all of the string types.
if (flavorStr.EqualsLiteral(kUnicodeMime)) {
LOGCLIP((" text targets\n"));
gtk_target_list_add_text_targets(list, 0);
continue;
}
if (nsContentUtils::IsFlavorImage(flavorStr)) {
// Don't bother adding image targets twice
if (!imagesAdded) {
// accept any writable image type
LOGCLIP((" image targets\n"));
gtk_target_list_add_image_targets(list, 0, TRUE);
imagesAdded = true;
}
continue;
}
// Add this to our list of valid targets
GdkAtom atom = gdk_atom_intern(flavorStr.get(), FALSE);
gtk_target_list_add(list, atom, 0, 0);
}
// Get GTK clipboard (CLIPBOARD or PRIMARY)
GtkClipboard* gtkClipboard =
gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
gint numTargets;
GtkTargetEntry* gtkTargets =
gtk_target_table_new_from_list(list, &numTargets);
LOGCLIP((" gtk_target_table_new_from_list() = %p\n", (void*)gtkTargets));
// Set getcallback and request to store data after an application exit
if (gtkTargets &&
gtk_clipboard_set_with_data(gtkClipboard, gtkTargets, numTargets,
clipboard_get_cb, clipboard_clear_cb, this)) {
LOGCLIP((" gtk_clipboard_set_with_data() is ok\n"));
// We managed to set-up the clipboard so update internal state
// We have to set it now because gtk_clipboard_set_with_data() calls
// clipboard_clear_cb() which reset our internal state
if (aWhichClipboard == kSelectionClipboard) {
mSelectionOwner = aOwner;
mSelectionTransferable = aTransferable;
} else {
mGlobalOwner = aOwner;
mGlobalTransferable = aTransferable;
gtk_clipboard_set_can_store(gtkClipboard, gtkTargets, numTargets);
}
rv = NS_OK;
} else {
LOGCLIP((" gtk_clipboard_set_with_data() failed!\n"));
rv = NS_ERROR_FAILURE;
}
gtk_target_table_free(gtkTargets, numTargets);
gtk_target_list_unref(list);
return rv;
}
void nsClipboard::SetTransferableData(nsITransferable* aTransferable,
nsCString& aFlavor,
const char* aClipboardData,
uint32_t aClipboardDataLength) {
LOGCLIP(("nsClipboard::SetTransferableData MIME %s\n", aFlavor.get()));
nsCOMPtr<nsISupports> wrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
aFlavor, aClipboardData, aClipboardDataLength, getter_AddRefs(wrapper));
aTransferable->SetTransferData(aFlavor.get(), wrapper);
}
NS_IMETHODIMP
nsClipboard::GetData(nsITransferable* aTransferable, int32_t aWhichClipboard) {
if (!aTransferable) return NS_ERROR_FAILURE;
LOGCLIP(("nsClipboard::GetData (%s)\n",
aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"));
// Get a list of flavors this transferable can import
nsTArray<nsCString> flavors;
nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
if (NS_FAILED(rv)) {
LOGCLIP((" FlavorsTransferableCanImport falied!\n"));
return rv;
}
for (uint32_t i = 0; i < flavors.Length(); i++) {
nsCString& flavorStr = flavors[i];
if (flavorStr.EqualsLiteral(kJPEGImageMime) ||
flavorStr.EqualsLiteral(kJPGImageMime) ||
flavorStr.EqualsLiteral(kPNGImageMime) ||
flavorStr.EqualsLiteral(kGIFImageMime)) {
// Emulate support for image/jpg
if (flavorStr.EqualsLiteral(kJPGImageMime)) {
flavorStr.Assign(kJPEGImageMime);
}
uint32_t clipboardDataLength;
const char* clipboardData = mContext->GetClipboardData(
flavorStr.get(), aWhichClipboard, &clipboardDataLength);
if (!clipboardData) {
LOGCLIP((" %s type is missing\n", flavorStr.get()));
continue;
}
nsCOMPtr<nsIInputStream> byteStream;
NS_NewByteInputStream(getter_AddRefs(byteStream),
MakeSpan(clipboardData, clipboardDataLength),
NS_ASSIGNMENT_COPY);
aTransferable->SetTransferData(flavorStr.get(), byteStream);
LOGCLIP((" got %s MIME data\n", flavorStr.get()));
mContext->ReleaseClipboardData(clipboardData);
return NS_OK;
}
// Special case text/unicode since we can convert any
// string into text/unicode
if (flavorStr.EqualsLiteral(kUnicodeMime)) {
const char* clipboardData = mContext->GetClipboardText(aWhichClipboard);
if (!clipboardData) {
LOGCLIP((" failed to get unicode data\n"));
// If the type was text/unicode and we couldn't get
// text off the clipboard, run the next loop
// iteration.
continue;
}
// Convert utf-8 into our unicode format.
NS_ConvertUTF8toUTF16 ucs2string(clipboardData);
const char* unicodeData = (const char*)ToNewUnicode(ucs2string);
uint32_t unicodeDataLength = ucs2string.Length() * 2;
SetTransferableData(aTransferable, flavorStr, unicodeData,
unicodeDataLength);
free((void*)unicodeData);
LOGCLIP((" got unicode data, length %d\n", ucs2string.Length()));
mContext->ReleaseClipboardData(clipboardData);
return NS_OK;
}
uint32_t clipboardDataLength;
const char* clipboardData = mContext->GetClipboardData(
flavorStr.get(), aWhichClipboard, &clipboardDataLength);
#ifdef MOZ_LOGGING
if (!clipboardData) {
LOGCLIP((" %s type is missing\n", flavorStr.get()));
}
#endif
if (clipboardData) {
LOGCLIP((" got %s mime type data.\n", flavorStr.get()));
// Special case text/html since we can convert into UCS2
if (flavorStr.EqualsLiteral(kHTMLMime)) {
char16_t* htmlBody = nullptr;
int32_t htmlBodyLen = 0;
// Convert text/html into our unicode format
ConvertHTMLtoUCS2(clipboardData, clipboardDataLength, &htmlBody,
htmlBodyLen);
// Try next data format?
if (!htmlBodyLen) {
mContext->ReleaseClipboardData(clipboardData);
continue;
}
SetTransferableData(aTransferable, flavorStr, (const char*)htmlBody,
htmlBodyLen * 2);
free(htmlBody);
} else {
SetTransferableData(aTransferable, flavorStr, clipboardData,
clipboardDataLength);
}
mContext->ReleaseClipboardData(clipboardData);
return NS_OK;
}
}
LOGCLIP((" failed to get clipboard content.\n"));
return NS_OK;
}
NS_IMETHODIMP
nsClipboard::EmptyClipboard(int32_t aWhichClipboard) {
LOGCLIP(("nsClipboard::EmptyClipboard (%s)\n",
aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"));
if (aWhichClipboard == kSelectionClipboard) {
if (mSelectionOwner) {
mSelectionOwner->LosingOwnership(mSelectionTransferable);
mSelectionOwner = nullptr;
}
mSelectionTransferable = nullptr;
} else {
if (mGlobalOwner) {
mGlobalOwner->LosingOwnership(mGlobalTransferable);
mGlobalOwner = nullptr;
}
mGlobalTransferable = nullptr;
}
return NS_OK;
}
NS_IMETHODIMP
nsClipboard::HasDataMatchingFlavors(const nsTArray<nsCString>& aFlavorList,
int32_t aWhichClipboard, bool* _retval) {
if (!_retval) {
return NS_ERROR_NULL_POINTER;
}
LOGCLIP(("nsClipboard::HasDataMatchingFlavors (%s)\n",
aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"));
*_retval = false;
int targetNums;
GdkAtom* targets = mContext->GetTargets(aWhichClipboard, &targetNums);
if (!targets) {
LOGCLIP((" no targes at clipboard (null)\n"));
return NS_OK;
}
// Walk through the provided types and try to match it to a
// provided type.
for (auto& flavor : aFlavorList) {
// We special case text/unicode here.
if (flavor.EqualsLiteral(kUnicodeMime) &&
gtk_targets_include_text(targets, targetNums)) {
*_retval = true;
LOGCLIP((" has kUnicodeMime\n"));
break;
}
for (int32_t j = 0; j < targetNums; j++) {
gchar* atom_name = gdk_atom_name(targets[j]);
if (!atom_name) continue;
if (flavor.Equals(atom_name)) {
*_retval = true;
LOGCLIP((" has %s\n", atom_name));
}
// X clipboard supports image/jpeg, but we want to emulate support
// for image/jpg as well
else if (flavor.EqualsLiteral(kJPGImageMime) &&
!strcmp(atom_name, kJPEGImageMime)) {
*_retval = true;
LOGCLIP((" has image/jpg\n"));
}
g_free(atom_name);
if (*_retval) break;
}
}
#ifdef MOZ_LOGGING
if (!(*_retval)) {
LOGCLIP((" no targes at clipboard (bad match)\n"));
}
#endif
g_free(targets);
return NS_OK;
}
NS_IMETHODIMP
nsClipboard::SupportsSelectionClipboard(bool* _retval) {
*_retval = mContext->HasSelectionSupport();
return NS_OK;
}
NS_IMETHODIMP
nsClipboard::SupportsFindClipboard(bool* _retval) {
*_retval = false;
return NS_OK;
}
nsITransferable* nsClipboard::GetTransferable(int32_t aWhichClipboard) {
nsITransferable* retval;
if (aWhichClipboard == kSelectionClipboard)
retval = mSelectionTransferable.get();
else
retval = mGlobalTransferable.get();
return retval;
}
void nsClipboard::SelectionGetEvent(GtkClipboard* aClipboard,
GtkSelectionData* aSelectionData) {
// Someone has asked us to hand them something. The first thing
// that we want to do is see if that something includes text. If
// it does, try to give it text/unicode after converting it to
// utf-8.
int32_t whichClipboard;
// which clipboard?
GdkAtom selection = gtk_selection_data_get_selection(aSelectionData);
if (selection == GDK_SELECTION_PRIMARY)
whichClipboard = kSelectionClipboard;
else if (selection == GDK_SELECTION_CLIPBOARD)
whichClipboard = kGlobalClipboard;
else
return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
LOGCLIP(("nsClipboard::SelectionGetEvent (%s)\n",
whichClipboard == kSelectionClipboard ? "primary" : "clipboard"));
nsCOMPtr<nsITransferable> trans = GetTransferable(whichClipboard);
if (!trans) {
// We have nothing to serve
LOGCLIP(("nsClipboard::SelectionGetEvent() - %s clipboard is empty!\n",
whichClipboard == kSelectionClipboard ? "Primary" : "Clipboard"));
return;
}
nsresult rv;
nsCOMPtr<nsISupports> item;
GdkAtom selectionTarget = gtk_selection_data_get_target(aSelectionData);
// Check to see if the selection data is some text type.
if (gtk_targets_include_text(&selectionTarget, 1)) {
// Try to convert our internal type into a text string. Get
// the transferable for this clipboard and try to get the
// text/unicode type for it.
rv = trans->GetTransferData("text/unicode", getter_AddRefs(item));
if (NS_FAILED(rv) || !item) {
return;
}
nsCOMPtr<nsISupportsString> wideString;
wideString = do_QueryInterface(item);
if (!wideString) return;
nsAutoString ucs2string;
wideString->GetData(ucs2string);
NS_ConvertUTF16toUTF8 utf8string(ucs2string);
gtk_selection_data_set_text(aSelectionData, utf8string.get(),
utf8string.Length());
return;
}
// Check to see if the selection data is an image type
if (gtk_targets_include_image(&selectionTarget, 1, TRUE)) {
// Look through our transfer data for the image
static const char* const imageMimeTypes[] = {kNativeImageMime,
kPNGImageMime, kJPEGImageMime,
kJPGImageMime, kGIFImageMime};
nsCOMPtr<nsISupports> imageItem;
nsCOMPtr<imgIContainer> image;
for (uint32_t i = 0; i < ArrayLength(imageMimeTypes); i++) {
rv = trans->GetTransferData(imageMimeTypes[i], getter_AddRefs(imageItem));
if (NS_FAILED(rv)) {
continue;
}
image = do_QueryInterface(imageItem);
if (image) {
break;
}
}
if (!image) { // Not getting an image for an image mime type!?
return;
}
GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(image);
if (!pixbuf) return;
gtk_selection_data_set_pixbuf(aSelectionData, pixbuf);
g_object_unref(pixbuf);
return;
}
if (selectionTarget == gdk_atom_intern(kHTMLMime, FALSE)) {
rv = trans->GetTransferData(kHTMLMime, getter_AddRefs(item));
if (NS_FAILED(rv) || !item) {
return;
}
nsCOMPtr<nsISupportsString> wideString;
wideString = do_QueryInterface(item);
if (!wideString) {
return;
}
nsAutoString ucs2string;
wideString->GetData(ucs2string);
nsAutoCString html;
// Add the prefix so the encoding is correctly detected.
html.AppendLiteral(kHTMLMarkupPrefix);
AppendUTF16toUTF8(ucs2string, html);
gtk_selection_data_set(aSelectionData, selectionTarget, 8,
(const guchar*)html.get(), html.Length());
return;
}
// Try to match up the selection data target to something our
// transferable provides.
gchar* target_name = gdk_atom_name(selectionTarget);
if (!target_name) return;
rv = trans->GetTransferData(target_name, getter_AddRefs(item));
// nothing found?
if (NS_FAILED(rv) || !item) {
g_free(target_name);
return;
}
void* primitive_data = nullptr;
uint32_t dataLen = 0;
nsPrimitiveHelpers::CreateDataFromPrimitive(nsDependentCString(target_name),
item, &primitive_data, &dataLen);
if (primitive_data) {
gtk_selection_data_set(aSelectionData, selectionTarget,
8, /* 8 bits in a unit */
(const guchar*)primitive_data, dataLen);
free(primitive_data);
}
g_free(target_name);
}
void nsClipboard::SelectionClearEvent(GtkClipboard* aGtkClipboard) {
int32_t whichClipboard = GetGeckoClipboardType(aGtkClipboard);
if (whichClipboard < 0) {
return;
}
LOGCLIP(("nsClipboard::SelectionClearEvent (%s)\n",
whichClipboard == kSelectionClipboard ? "primary" : "clipboard"));
EmptyClipboard(whichClipboard);
}
void clipboard_get_cb(GtkClipboard* aGtkClipboard,
GtkSelectionData* aSelectionData, guint info,
gpointer user_data) {
LOGCLIP(("clipboard_get_cb() callback\n"));
nsClipboard* aClipboard = static_cast<nsClipboard*>(user_data);
aClipboard->SelectionGetEvent(aGtkClipboard, aSelectionData);
}
void clipboard_clear_cb(GtkClipboard* aGtkClipboard, gpointer user_data) {
LOGCLIP(("clipboard_clear_cb() callback\n"));
nsClipboard* aClipboard = static_cast<nsClipboard*>(user_data);
aClipboard->SelectionClearEvent(aGtkClipboard);
}
/*
* when copy-paste, mozilla wants data encoded using UCS2,
* other app such as StarOffice use "text/html"(RFC2854).
* This function convert data(got from GTK clipboard)
* to data mozilla wanted.
*
* data from GTK clipboard can be 3 forms:
* 1. From current mozilla
* "text/html", charset = utf-16
* 2. From old version mozilla or mozilla-based app
* content("body" only), charset = utf-16
* 3. From other app who use "text/html" when copy-paste
* "text/html", has "charset" info
*
* data : got from GTK clipboard
* dataLength: got from GTK clipboard
* body : pass to Mozilla
* bodyLength: pass to Mozilla
*/
void ConvertHTMLtoUCS2(const char* data, int32_t dataLength,
char16_t** unicodeData, int32_t& outUnicodeLen) {
nsAutoCString charset;
GetHTMLCharset(data, dataLength, charset); // get charset of HTML
if (charset.EqualsLiteral("UTF-16")) { // current mozilla
outUnicodeLen = (dataLength / 2) - 1;
*unicodeData = reinterpret_cast<char16_t*>(
moz_xmalloc((outUnicodeLen + sizeof('\0')) * sizeof(char16_t)));
memcpy(*unicodeData, data + sizeof(char16_t),
outUnicodeLen * sizeof(char16_t));
(*unicodeData)[outUnicodeLen] = '\0';
} else if (charset.EqualsLiteral("UNKNOWN")) {
outUnicodeLen = 0;
return;
} else {
// app which use "text/html" to copy&paste
// get the decoder
auto encoding = Encoding::ForLabelNoReplacement(charset);
if (!encoding) {
#ifdef DEBUG_CLIPBOARD
g_print(" get unicode decoder error\n");
#endif
outUnicodeLen = 0;
return;
}
auto dataSpan = MakeSpan(data, dataLength);
// Remove kHTMLMarkupPrefix again, it won't necessarily cause any
// issues, but might confuse other users.
const size_t prefixLen = ArrayLength(kHTMLMarkupPrefix) - 1;
if (dataSpan.Length() >= prefixLen &&
Substring(data, prefixLen).EqualsLiteral(kHTMLMarkupPrefix)) {
dataSpan = dataSpan.From(prefixLen);
}
auto decoder = encoding->NewDecoder();
CheckedInt<size_t> needed =
decoder->MaxUTF16BufferLength(dataSpan.Length());
if (!needed.isValid() || needed.value() > INT32_MAX) {
outUnicodeLen = 0;
return;
}
outUnicodeLen = 0;
if (needed.value()) {
*unicodeData = reinterpret_cast<char16_t*>(
moz_xmalloc((needed.value() + 1) * sizeof(char16_t)));
uint32_t result;
size_t read;
size_t written;
bool hadErrors;
Tie(result, read, written, hadErrors) = decoder->DecodeToUTF16(
AsBytes(dataSpan), MakeSpan(*unicodeData, needed.value()), true);
MOZ_ASSERT(result == kInputEmpty);
MOZ_ASSERT(read == size_t(dataSpan.Length()));
MOZ_ASSERT(written <= needed.value());
Unused << hadErrors;
outUnicodeLen = written;
// null terminate.
(*unicodeData)[outUnicodeLen] = '\0';
} // if valid length
}
}
/*
* get "charset" information from clipboard data
* return value can be:
* 1. "UTF-16": mozilla or "text/html" with "charset=utf-16"
* 2. "UNKNOWN": mozilla can't detect what encode it use
* 3. other: "text/html" with other charset than utf-16
*/
void GetHTMLCharset(const char* data, int32_t dataLength, nsCString& str) {
// if detect "FFFE" or "FEFF", assume UTF-16
char16_t* beginChar = (char16_t*)data;
if ((beginChar[0] == 0xFFFE) || (beginChar[0] == 0xFEFF)) {
str.AssignLiteral("UTF-16");
return;
}
// no "FFFE" and "FEFF", assume ASCII first to find "charset" info
const nsDependentCSubstring htmlStr(data, dataLength);
nsACString::const_iterator start, end;
htmlStr.BeginReading(start);
htmlStr.EndReading(end);
nsACString::const_iterator valueStart(start), valueEnd(start);
if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("CONTENT=\"text/html;"),
start, end)) {
start = end;
htmlStr.EndReading(end);
if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("charset="), start,
end)) {
valueStart = end;
start = end;
htmlStr.EndReading(end);
if (FindCharInReadable('"', start, end)) valueEnd = start;
}
}
// find "charset" in HTML
if (valueStart != valueEnd) {
str = Substring(valueStart, valueEnd);
ToUpperCase(str);
#ifdef DEBUG_CLIPBOARD
printf("Charset of HTML = %s\n", charsetUpperStr.get());
#endif
return;
}
str.AssignLiteral("UNKNOWN");
}