From 5a2db0883cc6acadebf3fa0197c36d81af167492 Mon Sep 17 00:00:00 2001 From: Peter Varga Date: Thu, 11 Nov 2021 14:24:48 +0100 Subject: Add CF_HTML clipboard format handling Fixes: QTBUG-92539 Change-Id: Iece974e7b045bd793ceb8870f370803bf2524c33 Reviewed-by: Allan Sandfeld Jensen (cherry picked from commit 74021bfcb8f3937e960e76c39d06f5a2a6304673) Reviewed-by: Qt Cherry-pick Bot --- src/core/CMakeLists.txt | 5 ++ src/core/clipboard_qt.cpp | 44 +++++++++++++ src/core/clipboard_util_win.cpp | 140 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 src/core/clipboard_util_win.cpp diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2143b8fad..f287ac3ba 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -256,6 +256,11 @@ foreach(arch ${archs}) renderer/extensions/resource_request_policy_qt.cpp renderer/extensions/resource_request_policy_qt.h ) + extend_gn_target(${buildGn} CONDITION WIN32 + SOURCES + clipboard_util_win.cpp + ) + ## # GN PARAMETERS SETUP ## diff --git a/src/core/clipboard_qt.cpp b/src/core/clipboard_qt.cpp index 026653cbb..6549a19da 100644 --- a/src/core/clipboard_qt.cpp +++ b/src/core/clipboard_qt.cpp @@ -46,6 +46,7 @@ #include "type_conversion.h" #include "base/logging.h" +#include "base/strings/utf_offset_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/clipboard/custom_data_helper.h" @@ -107,6 +108,12 @@ Clipboard *Clipboard::Create() namespace QtWebEngineCore { +#if defined(Q_OS_WIN) +extern std::string HtmlToCFHtml(const std::string &html, const std::string &base_url); +extern void CFHtmlExtractMetadata(const std::string &cf_html, std::string *base_url, + size_t *html_start, size_t *fragment_start, size_t *fragment_end); +#endif // defined(Q_OS_WIN) + void ClipboardQt::WritePortableRepresentations(ui::ClipboardBuffer type, const ObjectMap &objects, std::unique_ptr data_src) { DCHECK(CalledOnValidThread()); @@ -157,7 +164,17 @@ void ClipboardQt::WriteHTML(const char *markup_data, size_t markup_len, const ch // Mirrors the behavior in ui/base/clipboard/clipboard_mac.mm in Chromium. markup_string.prepend(QLatin1String("")); #endif + +#if !defined(Q_OS_WIN) getUncommittedData()->setHtml(markup_string); +#else + std::string url; + if (url_len > 0) + url.assign(url_data, url_len); + + std::string cf_html = HtmlToCFHtml(markup_string.toStdString(), url); + getUncommittedData()->setHtml(QString::fromStdString(cf_html)); +#endif // !defined(Q_OS_WIN) } void ClipboardQt::WriteRTF(const char *rtf_data, size_t data_len) @@ -269,8 +286,35 @@ void ClipboardQt::ReadHTML(ui::ClipboardBuffer type, type == ui::ClipboardBuffer::kCopyPaste ? QClipboard::Clipboard : QClipboard::Selection); if (!mimeData) return; + +#if !defined(Q_OS_WIN) *markup = toString16(mimeData->html()); *fragment_end = static_cast(markup->length()); +#else + const std::string cf_html = mimeData->html().toStdString(); + size_t html_start = std::string::npos; + size_t start_index = std::string::npos; + size_t end_index = std::string::npos; + CFHtmlExtractMetadata(cf_html, src_url, &html_start, &start_index, &end_index); + + // This might happen if the contents of the clipboard changed and CF_HTML is + // no longer available. + if (start_index == std::string::npos || end_index == std::string::npos + || html_start == std::string::npos) + return; + + if (start_index < html_start || end_index < start_index) + return; + + std::vector offsets; + offsets.push_back(start_index - html_start); + offsets.push_back(end_index - html_start); + markup->assign(base::UTF8ToUTF16AndAdjustOffsets(cf_html.data() + html_start, &offsets)); + // Ensure the Fragment points within the string; see https://crbug.com/607181. + size_t end = std::min(offsets[1], markup->length()); + *fragment_start = base::checked_cast(std::min(offsets[0], end)); + *fragment_end = base::checked_cast(end); +#endif // !defined(Q_OS_WIN) } void ClipboardQt::ReadRTF(ui::ClipboardBuffer type, diff --git a/src/core/clipboard_util_win.cpp b/src/core/clipboard_util_win.cpp new file mode 100644 index 000000000..ae615b3b6 --- /dev/null +++ b/src/core/clipboard_util_win.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE.Chromium file. + +#include + +#include "base/strings/stringprintf.h" +#include "base/strings/string_util.h" + +// These functions are copied from ui/base/clipboard/clipboard_util_win.cc + +namespace QtWebEngineCore { + +// Helper method for converting from text/html to MS CF_HTML. +// Documentation for the CF_HTML format is available at +// http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx +// HtmlToCFHtml is based on similar method in +// WebCore/platform/win/ClipboardUtilitiesWin.cpp. +std::string HtmlToCFHtml(const std::string &html, const std::string &base_url) +{ + if (html.empty()) + return std::string(); + +#define MAX_DIGITS 10 +#define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits) +#define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u" +#define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS) + + static const char *header = "Version:0.9\r\n" + "StartHTML:" NUMBER_FORMAT "\r\n" + "EndHTML:" NUMBER_FORMAT "\r\n" + "StartFragment:" NUMBER_FORMAT "\r\n" + "EndFragment:" NUMBER_FORMAT "\r\n"; + static const char *source_url_prefix = "SourceURL:"; + + static const char *start_markup = "\r\n\r\n"; + static const char *end_markup = "\r\n\r\n"; + + // Calculate offsets + size_t start_html_offset = strlen(header) - strlen(NUMBER_FORMAT) * 4 + MAX_DIGITS * 4; + if (!base_url.empty()) { + start_html_offset += strlen(source_url_prefix) + base_url.length() + 2; // Add 2 for \r\n. + } + size_t start_fragment_offset = start_html_offset + strlen(start_markup); + size_t end_fragment_offset = start_fragment_offset + html.length(); + size_t end_html_offset = end_fragment_offset + strlen(end_markup); + + std::string result = base::StringPrintf(header, start_html_offset, end_html_offset, + start_fragment_offset, end_fragment_offset); + if (!base_url.empty()) { + result += source_url_prefix; + result += base_url; + result += "\r\n"; + } + result += start_markup; + result += html; + result += end_markup; + +#undef MAX_DIGITS +#undef MAKE_NUMBER_FORMAT_1 +#undef MAKE_NUMBER_FORMAT_2 +#undef NUMBER_FORMAT + + return result; +} + +void CFHtmlExtractMetadata(const std::string &cf_html, std::string *base_url, size_t *html_start, + size_t *fragment_start, size_t *fragment_end) +{ + // Obtain base_url if present. + if (base_url) { + static constexpr char kSrcUrlStr[] = "SourceURL:"; + size_t line_start = cf_html.find(kSrcUrlStr); + if (line_start != std::string::npos) { + size_t src_end = cf_html.find("\n", line_start); + size_t src_start = line_start + strlen(kSrcUrlStr); + if (src_end != std::string::npos && src_start != std::string::npos) { + *base_url = cf_html.substr(src_start, src_end - src_start); + base::TrimWhitespaceASCII(*base_url, base::TRIM_ALL, base_url); + } + } + } + + // Find the markup between "" and "". + // If the comments cannot be found, like copying from OpenOffice Writer, + // we simply fall back to using StartFragment/EndFragment bytecount values + // to determine the fragment indexes. + std::string cf_html_lower = base::ToLowerASCII(cf_html); + size_t markup_start = cf_html_lower.find("( + atoi(cf_html.c_str() + start_fragment_start + strlen(kStartFragmentStr))); + } + + static constexpr char kEndFragmentStr[] = "EndFragment:"; + size_t end_fragment_start = cf_html.find(kEndFragmentStr); + if (end_fragment_start != std::string::npos) { + *fragment_end = static_cast( + atoi(cf_html.c_str() + end_fragment_start + strlen(kEndFragmentStr))); + } + } else { + *fragment_start = cf_html.find('>', tag_start) + 1; + size_t tag_end = cf_html.rfind("