/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:expandtab:shiftwidth=2:tabstop=2:
*/ /* 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 <gtk/gtk.h> #ifdefined(MOZ_X11) # include <gtk/gtkx.h> #endif
#include"mozilla/Encoding.h"
usingnamespace mozilla;
// Idle timeout for receiving selection and property notify events (microsec) // Right now it's set to 1 sec. constint kClipboardTimeout = 1000000;
// Defines how many event loop iterations will be done without sleep. // We ususally get data in first 2-3 iterations unless some large object // (an image for instance) is transferred through clipboard. constint kClipboardFastIterationNum = 3;
// We add this prefix to HTML markup, so that GetHTMLCharset can correctly // detect the HTML as UTF-8 encoded. staticconstchar kHTMLMarkupPrefix[] =
R"()";
// Callback when someone asks us for the data staticvoid clipboard_get_cb(GtkClipboard* aGtkClipboard,
GtkSelectionData* aSelectionData, guint info,
gpointer user_data);
// Callback when someone asks us to clear a clipboard staticvoid clipboard_clear_cb(GtkClipboard* aGtkClipboard, gpointer user_data);
// Callback when owner of clipboard data is changed staticvoid clipboard_owner_change_cb(GtkClipboard* aGtkClipboard,
GdkEventOwnerChange* aEvent,
gpointer aUserData);
nsresult nsClipboard::Init(void) { #ifdefined(MOZ_X11) if (widget::GdkIsX11Display()) {
mContext = new nsRetrievalContextX11();
} #endif #ifdefined(MOZ_WAYLAND) if (widget::GdkIsWaylandDisplay()) {
mContext = new nsRetrievalContextWayland();
} #endif
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) {
os->AddObserver(this, "xpcom-shutdown", false);
}
return NS_OK;
}
NS_IMETHODIMP
nsClipboard::Observe(nsISupports* aSubject, constchar* aTopic, const char16_t* aData) { // Save global clipboard content to CLIPBOARD_MANAGER. // gtk_clipboard_store() can run an event loop, so call from a dedicated // runnable. return SchedulerGroup::Dispatch(
NS_NewRunnableFunction("gtk_clipboard_store()", []() {
MOZ_CLIPBOARD_LOG("nsClipboard storing clipboard content\n");
gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
}));
}
// See if we can short cut if ((aWhichClipboard == kGlobalClipboard &&
aTransferable == mGlobalTransferable.get()) ||
(aWhichClipboard == kSelectionClipboard &&
aTransferable == mSelectionTransferable.get())) { return NS_OK;
}
// 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)) {
MOZ_CLIPBOARD_LOG(" FlavorsTransferableCanExport failed!\n"); // Fall through. |gtkTargets| will be null below.
}
// 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];
MOZ_CLIPBOARD_LOG(" processing target %s\n", flavorStr.get());
// Special case text/plain since we can handle all of the string types. if (flavorStr.EqualsLiteral(kTextMime)) {
MOZ_CLIPBOARD_LOG(" adding 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
MOZ_CLIPBOARD_LOG(" adding IMAGE targets\n");
gtk_target_list_add_image_targets(list, 0, TRUE);
imagesAdded = true;
} continue;
}
if (flavorStr.EqualsLiteral(kFileMime)) {
MOZ_CLIPBOARD_LOG(" adding text/uri-list target\n");
GdkAtom atom = gdk_atom_intern(kURIListMime, FALSE);
gtk_target_list_add(list, atom, 0, 0); continue;
}
// Add this to our list of valid targets
MOZ_CLIPBOARD_LOG(" adding OTHER target %s\n", flavorStr.get());
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 = 0;
GtkTargetEntry* gtkTargets =
gtk_target_table_new_from_list(list, &numTargets); if (!gtkTargets || numTargets == 0) {
MOZ_CLIPBOARD_LOG( " gtk_target_table_new_from_list() failed or empty list of " "targets!\n"); // Clear references to the any old data and let GTK know that it is no // longer available.
EmptyNativeClipboardData(aWhichClipboard); return NS_ERROR_FAILURE;
}
ClearCachedTargets(aWhichClipboard);
// Set getcallback and request to store data after an application exit if (gtk_clipboard_set_with_data(gtkClipboard, gtkTargets, numTargets,
clipboard_get_cb, clipboard_clear_cb, this)) { // 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) {
mSelectionSequenceNumber++;
mSelectionTransferable = aTransferable;
} else {
mGlobalSequenceNumber++;
mGlobalTransferable = aTransferable;
gtk_clipboard_set_can_store(gtkClipboard, gtkTargets, numTargets);
}
// When clipboard contains only images, X11/Gtk tries to convert them // to text when we request text instead of just fail to provide the data. // So if clipboard contains images only remove text MIME offer. bool nsClipboard::FilterImportedFlavors(int32_t aWhichClipboard,
nsTArray<nsCString>& aFlavors) {
MOZ_CLIPBOARD_LOG("nsClipboard::FilterImportedFlavors");
auto targets = mContext->GetTargets(aWhichClipboard); if (!targets) {
MOZ_CLIPBOARD_LOG(" X11: no targes at clipboard (null), quit.\n"); returntrue;
}
for (constauto& atom : targets.AsSpan()) {
GUniquePtr<gchar> atom_name(gdk_atom_name(atom)); if (!atom_name) { continue;
} // Filter out system MIME types. if (strcmp(atom_name.get(), "TARGETS") == 0 ||
strcmp(atom_name.get(), "TIMESTAMP") == 0 ||
strcmp(atom_name.get(), "SAVE_TARGETS") == 0 ||
strcmp(atom_name.get(), "MULTIPLE") == 0) { continue;
} // Filter out types which can't be converted to text. if (strncmp(atom_name.get(), "image/", 6) == 0 ||
strncmp(atom_name.get(), "application/", 12) == 0 ||
strncmp(atom_name.get(), "audio/", 6) == 0 ||
strncmp(atom_name.get(), "video/", 6) == 0) { continue;
} // We have some other MIME type on clipboard which can be hopefully // converted to text without any problem.
MOZ_CLIPBOARD_LOG( " X11: text types in clipboard, no need to filter them.\n"); returntrue;
}
// So make sure we offer only types we have at clipboard.
nsTArray<nsCString> clipboardFlavors; for (constauto& atom : targets.AsSpan()) {
GUniquePtr<gchar> atom_name(gdk_atom_name(atom)); if (!atom_name) { continue;
} if (IsMIMEAtFlavourList(aFlavors, atom_name.get())) {
clipboardFlavors.AppendElement(nsCString(atom_name.get()));
}
}
aFlavors.SwapElements(clipboardFlavors); #ifdef MOZ_LOGGING
MOZ_CLIPBOARD_LOG(" X11: Flavors which match clipboard content:\n"); for (uint32_t i = 0; i < aFlavors.Length(); i++) {
MOZ_CLIPBOARD_LOG(" %s\n", aFlavors[i].get());
} #endif returntrue;
}
static nsresult GetTransferableFlavors(nsITransferable* aTransferable,
nsTArray<nsCString>& aFlavors) { if (!aTransferable) { return NS_ERROR_FAILURE;
} // Get a list of flavors this transferable can import
nsresult rv = aTransferable->FlavorsTransferableCanImport(aFlavors); if (NS_FAILED(rv)) {
MOZ_CLIPBOARD_LOG(" FlavorsTransferableCanImport falied!\n"); return rv;
} #ifdef MOZ_LOGGING
MOZ_CLIPBOARD_LOG(" Flavors which can be imported:"); for (constauto& flavor : aFlavors) {
MOZ_CLIPBOARD_LOG(" %s", flavor.get());
} #endif return NS_OK;
}
staticbool TransferableSetFile(nsITransferable* aTransferable, const nsACString& aURIList) {
nsresult rv;
nsTArray<nsCString> uris = mozilla::widget::ParseTextURIList(aURIList); if (!uris.IsEmpty()) {
nsCOMPtr<nsIURI> fileURI;
NS_NewURI(getter_AddRefs(fileURI), uris[0]); if (nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI, &rv)) {
nsCOMPtr<nsIFile> file;
rv = fileURL->GetFile(getter_AddRefs(file)); if (NS_SUCCEEDED(rv)) {
aTransferable->SetTransferData(kFileMime, file);
MOZ_CLIPBOARD_LOG(" successfully set file to clipboard\n"); returntrue;
}
}
} returnfalse;
}
// Convert text/html into our text format
nsAutoCString charset; if (!GetHTMLCharset(aData, charset)) { // Fall back to utf-8 in case html/data is missing kHTMLMarkupPrefix.
MOZ_CLIPBOARD_LOG( "Failed to get html/text encoding, fall back to utf-8.\n");
charset.AssignLiteral("utf-8");
}
MOZ_CLIPBOARD_LOG("TransferableSetHTML: HTML detected charset %s",
charset.get()); // app which use "text/html" to copy&paste // get the decoder auto encoding = Encoding::ForLabelNoReplacement(charset); if (!encoding) {
MOZ_CLIPBOARD_LOG( "TransferableSetHTML: get unicode decoder error (charset: %s)",
charset.get()); returnfalse;
}
// Filter out MIME types on X11 to prevent unwanted conversions, // see Bug 1611407 if (widget::GdkIsX11Display() &&
!FilterImportedFlavors(aWhichClipboard, flavors)) {
MOZ_CLIPBOARD_LOG(" Missing suitable clipboard data, quit."); return NS_OK;
}
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);
}
auto clipboardData =
mContext->GetClipboardData(flavorStr.get(), aWhichClipboard); if (!clipboardData) {
MOZ_CLIPBOARD_LOG(" %s type is missing\n", flavorStr.get()); continue;
}
// Special case text/plain since we can convert any // string into text/plain if (flavorStr.EqualsLiteral(kTextMime)) {
MOZ_CLIPBOARD_LOG(" Getting text %s MIME clipboard data\n",
flavorStr.get());
auto clipboardData = mContext->GetClipboardText(aWhichClipboard); if (!clipboardData) {
MOZ_CLIPBOARD_LOG(" failed to get text data\n"); // If the type was text/plain and we couldn't get // text off the clipboard, run the next loop // iteration. continue;
}
// Convert utf-8 into our text format.
NS_ConvertUTF8toUTF16 ucs2string(clipboardData.get());
SetTransferableData(aTransferable, flavorStr,
(constchar*)ucs2string.BeginReading(),
ucs2string.Length() * 2);
MOZ_CLIPBOARD_LOG(" got text data, length %zd\n", ucs2string.Length()); return NS_OK;
}
if (flavorStr.EqualsLiteral(kFileMime)) {
MOZ_CLIPBOARD_LOG(" Getting %s file clipboard data\n",
flavorStr.get());
auto clipboardData =
mContext->GetClipboardData(kURIListMime, aWhichClipboard); if (!clipboardData) {
MOZ_CLIPBOARD_LOG(" text/uri-list type is missing\n"); continue;
}
auto clipboardData =
mContext->GetClipboardData(flavorStr.get(), aWhichClipboard);
#ifdef MOZ_LOGGING if (!clipboardData) {
MOZ_CLIPBOARD_LOG(" %s type is missing\n", flavorStr.get());
} #endif
if (clipboardData) {
MOZ_CLIPBOARD_LOG(" got %s mime type data.\n", flavorStr.get());
// Special case text/html since we can convert into UCS2 if (flavorStr.EqualsLiteral(kHTMLMime)) { if (!TransferableSetHTML(aTransferable, clipboardData.AsSpan())) { continue;
}
} else { auto span = clipboardData.AsSpan();
SetTransferableData(aTransferable, flavorStr, span.data(),
span.Length());
} return NS_OK;
}
}
MOZ_CLIPBOARD_LOG(" failed to get clipboard content.\n"); return NS_OK;
}
auto flavorsNum = importedFlavors.Length(); if (!flavorsNum) {
aCallback(NS_OK); return;
} #ifdef MOZ_LOGGING if (flavorsNum > 1) {
MOZ_CLIPBOARD_LOG( " Only first MIME type (%s) will be imported from clipboard!",
importedFlavors[0].get());
} #endif
// Filter out MIME types on X11 to prevent unwanted conversions, // see Bug 1611407 if (widget::GdkIsX11Display()) {
AsyncHasNativeClipboardDataMatchingFlavors(
importedFlavors, aWhichClipboard,
[aWhichClipboard, transferable = nsCOMPtr{aTransferable},
callback = std::move(aCallback)](auto aResultOrError) mutable { if (aResultOrError.isErr()) {
callback(aResultOrError.unwrapErr()); return;
}
nsTArray<nsCString> clipboardFlavors =
std::move(aResultOrError.unwrap()); if (!clipboardFlavors.Length()) {
MOZ_CLIPBOARD_LOG(" no flavors in clipboard, quit.");
callback(NS_OK); return;
}
staticbool FlavorMatchesTarget(const nsACString& aFlavor, GdkAtom aTarget) {
GUniquePtr<gchar> atom_name(gdk_atom_name(aTarget)); if (!atom_name) { returnfalse;
} if (aFlavor.Equals(atom_name.get())) {
MOZ_CLIPBOARD_LOG(" has %s\n", atom_name.get()); returntrue;
} // X clipboard supports image/jpeg, but we want to emulate support // for image/jpg as well if (aFlavor.EqualsLiteral(kJPGImageMime) &&
!strcmp(atom_name.get(), kJPEGImageMime)) {
MOZ_CLIPBOARD_LOG(" has image/jpg\n"); returntrue;
} // application/x-moz-file should be treated like text/uri-list if (aFlavor.EqualsLiteral(kFileMime) &&
!strcmp(atom_name.get(), kURIListMime)) {
MOZ_CLIPBOARD_LOG( " has text/uri-list treating as application/x-moz-file"); returntrue;
} returnfalse;
}
auto targets = mContext->GetTargets(aWhichClipboard); if (!targets) {
MOZ_CLIPBOARD_LOG(" no targes at clipboard (null)\n"); returnfalse;
}
#ifdef MOZ_LOGGING if (MOZ_CLIPBOARD_LOG_ENABLED()) {
MOZ_CLIPBOARD_LOG(" Asking for content:\n"); for (auto& flavor : aFlavorList) {
MOZ_CLIPBOARD_LOG(" MIME %s\n", flavor.get());
}
MOZ_CLIPBOARD_LOG(" Clipboard content (target nums %zu):\n",
targets.AsSpan().Length()); for (constauto& target : targets.AsSpan()) {
GUniquePtr<gchar> atom_name(gdk_atom_name(target)); if (!atom_name) {
MOZ_CLIPBOARD_LOG(" failed to get MIME\n"); continue;
}
MOZ_CLIPBOARD_LOG(" MIME %s\n", atom_name.get());
}
} #endif
// Walk through the provided types and try to match it to a // provided type. for (auto& flavor : aFlavorList) { // We special case text/plain here. if (flavor.EqualsLiteral(kTextMime) &&
gtk_targets_include_text(targets.AsSpan().data(),
targets.AsSpan().Length())) {
MOZ_CLIPBOARD_LOG(" has kTextMime\n"); returntrue;
} for (constauto& target : targets.AsSpan()) { if (FlavorMatchesTarget(flavor, target)) { returntrue;
}
}
}
MOZ_CLIPBOARD_LOG(" no targes at clipboard (bad match)\n"); returnfalse;
}
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/plain 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; elseif (selection == GDK_SELECTION_CLIPBOARD)
whichClipboard = kGlobalClipboard; else return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
nsCOMPtr<nsITransferable> trans = GetTransferable(whichClipboard); if (!trans) { // We have nothing to serve
MOZ_CLIPBOARD_LOG( "nsClipboard::SelectionGetEvent() - %s clipboard is empty!\n",
whichClipboard == kSelectionClipboard ? "Primary" : "Clipboard"); return;
}
// Check to see if the selection data is some text type. if (gtk_targets_include_text(&selectionTarget, 1)) {
MOZ_CLIPBOARD_LOG(" providing text/plain data\n"); // Try to convert our internal type into a text string. Get // the transferable for this clipboard and try to get the // text/plain type for it.
rv = trans->GetTransferData("text/plain", getter_AddRefs(item)); if (NS_FAILED(rv) || !item) {
MOZ_CLIPBOARD_LOG(" GetTransferData() failed to get text/plain!\n"); return;
}
nsCOMPtr<nsISupportsString> wideString;
wideString = do_QueryInterface(item); if (!wideString) return;
MOZ_CLIPBOARD_LOG(" sent %zd bytes of utf-8 data\n", utf8string.Length()); if (selectionTarget == gdk_atom_intern("text/plain;charset=utf-8", FALSE)) {
MOZ_CLIPBOARD_LOG( " using gtk_selection_data_set for 'text/plain;charset=utf-8'\n"); // Bypass gtk_selection_data_set_text, which will convert \n to \r\n // in some versions of GTK.
gtk_selection_data_set(aSelectionData, selectionTarget, 8, reinterpret_cast<const guchar*>(utf8string.get()),
utf8string.Length());
} else {
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)) {
MOZ_CLIPBOARD_LOG(" providing image data\n"); // Look through our transfer data for the image staticconstchar* const imageMimeTypes[] = {kNativeImageMime,
kPNGImageMime, kJPEGImageMime,
kJPGImageMime, kGIFImageMime};
nsCOMPtr<nsISupports> imageItem;
nsCOMPtr<imgIContainer> image; for (uint32_t i = 0; i < std::size(imageMimeTypes); i++) {
rv = trans->GetTransferData(imageMimeTypes[i], getter_AddRefs(imageItem)); if (NS_FAILED(rv)) {
MOZ_CLIPBOARD_LOG(" %s is missing at GetTransferData()\n",
imageMimeTypes[i]); continue;
}
image = do_QueryInterface(imageItem); if (image) {
MOZ_CLIPBOARD_LOG(" %s is available at GetTransferData()\n",
imageMimeTypes[i]); break;
}
}
if (!image) { // Not getting an image for an image mime type!?
MOZ_CLIPBOARD_LOG( " Failed to get any image mime from GetTransferData()!\n"); return;
}
nsAutoCString html; // Add the prefix so the encoding is correctly detected.
html.AppendLiteral(kHTMLMarkupPrefix);
AppendUTF16toUTF8(ucs2string, html);
// We put kFileMime onto the clipboard as kURIListMime. if (selectionTarget == gdk_atom_intern(kURIListMime, FALSE)) {
MOZ_CLIPBOARD_LOG(" providing %s data\n", kURIListMime);
rv = trans->GetTransferData(kFileMime, getter_AddRefs(item)); if (NS_FAILED(rv) || !item) {
MOZ_CLIPBOARD_LOG(" failed to get %s data by GetTransferData()!\n",
kFileMime); return;
}
nsCOMPtr<nsIFile> file = do_QueryInterface(item); if (!file) {
MOZ_CLIPBOARD_LOG(" failed to get nsIFile interface!"); return;
}
nsCOMPtr<nsIURI> fileURI;
rv = NS_NewFileURI(getter_AddRefs(fileURI), file); if (NS_FAILED(rv)) {
MOZ_CLIPBOARD_LOG(" failed to get fileURI\n"); return;
}
nsAutoCString uri; if (NS_FAILED(fileURI->GetSpec(uri))) {
MOZ_CLIPBOARD_LOG(" failed to get fileURI spec\n"); return;
}
MOZ_CLIPBOARD_LOG(" Try if we have anything at GetTransferData() for %s\n",
GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get());
// Try to match up the selection data target to something our // transferable provides.
GUniquePtr<gchar> target_name(gdk_atom_name(selectionTarget)); if (!target_name) {
MOZ_CLIPBOARD_LOG(" Failed to get target name!\n"); return;
}
rv = trans->GetTransferData(target_name.get(), getter_AddRefs(item)); // nothing found? if (NS_FAILED(rv) || !item) {
MOZ_CLIPBOARD_LOG(" Failed to get anything from GetTransferData()!\n"); return;
}
void* primitive_data = nullptr;
uint32_t dataLen = 0;
nsPrimitiveHelpers::CreateDataFromPrimitive(
nsDependentCString(target_name.get()), item, &primitive_data, &dataLen); if (!primitive_data) {
MOZ_CLIPBOARD_LOG(" Failed to get primitive data!\n"); return;
}
MOZ_CLIPBOARD_LOG(" Setting %s as a primitive data type, %d bytes\n",
target_name.get(), dataLen);
gtk_selection_data_set(aSelectionData, selectionTarget,
8, /* 8 bits in a unit */
(const guchar*)primitive_data, dataLen);
free(primitive_data);
}
void nsClipboard::OwnerChangedEvent(GtkClipboard* aGtkClipboard,
GdkEventOwnerChange* aEvent) {
Maybe<ClipboardType> whichClipboard = GetGeckoClipboardType(aGtkClipboard); if (whichClipboard.isNothing()) { return;
}
MOZ_CLIPBOARD_LOG( "nsClipboard::OwnerChangedEvent (%s)\n",
*whichClipboard == kSelectionClipboard ? "primary" : "clipboard");
GtkWidget* gtkWidget = [aEvent]() -> GtkWidget* { if (!aEvent->owner) { return nullptr;
}
gpointer user_data = nullptr;
gdk_window_get_user_data(aEvent->owner, &user_data); return GTK_WIDGET(user_data);
}(); // If we can get GtkWidget from the current clipboard owner, this // owner-changed event must be triggered by ourself via calling // gtk_clipboard_set_with_data, the sequence number should already be handled. if (!gtkWidget) { if (*whichClipboard == kSelectionClipboard) {
mSelectionSequenceNumber++;
} else {
mGlobalSequenceNumber++;
}
}
/* * This function extracts the encoding label from the subset of HTML internal * encoding declaration syntax that uses the old long form with double quotes * and without spaces around the equals sign between the "content" attribute * name and the attribute value. * * This was added for the sake of an ancient version of StarOffice * in the pre-UTF-8 era in bug 123389. It is unclear if supporting * non-UTF-8 encodings is still necessary and if this function * still needs to exist. * * As of December 2022, both Gecko and LibreOffice emit an UTF-8 * declaration that this function successfully extracts "UTF-8" from, * but that's also the default that we fall back on if this function * fails to extract a label.
*/ bool GetHTMLCharset(Span<constchar> aData, nsCString& aFoundCharset) { // Assume ASCII first to find "charset" info const nsDependentCSubstring htmlStr(aData);
nsACString::const_iterator start, end;
htmlStr.BeginReading(start);
htmlStr.EndReading(end);
nsACString::const_iterator valueStart(start), valueEnd(start);
if (CaseInsensitiveFindInReadable("CONTENT=\"text/html;"_ns, start, end)) {
start = end;
htmlStr.EndReading(end);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.