Files
sousa-gecko/widget/android/nsClipboard.cpp
T
Daisuke Akatsuka 31e276101e Bug 1874203: Introduce nsIClipboard.getDataIfSmallerThan() r=mak,win-reviewers,geckoview-reviewers,nalexander,gstoll
Add a `getDataIfSmallerThan()` API to `nsIClipboard`. This API allows data to be retrieved only if the size of clipboard's contents is smaller than the specified threshold. The parameters are `aTransferable`, `aWhichClipboard` and `aRequestingWindowContext`, similar to [nsIClipboard.getData()](https://searchfox.org/firefox-main/rev/7709f0a26aeb3c39a5fd86e794425530f0d0b528/widget/nsIClipboard.idl#165-181). In addition, it takes `aThreshold`, which defines the threshold value. (Currently, it only targets plain/text content and checks its character count.)

The reason I want to add this is for use in the urlbar and places modules.
For example, we currently want to change the context menu items based on the clipboard's content. However, when the content is very large, it takes a significant amount of time to retrieve and parse it. [1]
Additionally, while there is a feature to suggest URLs when the clipboard contains a URL[2], we cannot enable this feature by default due to the same reason.
To resolve them, adds the above API, we want to make very large content ignorable.

[1] https://bugzilla.mozilla.org/show_bug.cgi?id=1874203
[2] https://bugzilla.mozilla.org/show_bug.cgi?id=1842230

Differential Revision: https://phabricator.services.mozilla.com/D280552
2026-05-25 03:19:09 +00:00

209 lines
6.4 KiB
C++

/* 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/java/ClipboardWrappers.h"
#include "mozilla/java/GeckoAppShellWrappers.h"
#include "nsClipboard.h"
#include "nsISupportsPrimitives.h"
#include "nsCOMPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsMemory.h"
#include "nsStringStream.h"
#include "nsPrimitiveHelpers.h"
using namespace mozilla;
NS_IMPL_ISUPPORTS_INHERITED0(nsClipboard, nsBaseClipboard)
/* The Android clipboard only supports text and doesn't support mime types
* so we assume all clipboard data is text/plain for now. Documentation
* indicates that support for other data types is planned for future
* releases.
*/
nsClipboard::nsClipboard()
: nsBaseClipboard(mozilla::dom::ClipboardCapabilities(
false /* supportsSelectionClipboard */,
false /* supportsFindClipboard */,
false /* supportsSelectionCache */)) {
java::Clipboard::StartTrackingClipboardData(
java::GeckoAppShell::GetApplicationContext());
}
nsClipboard::~nsClipboard() {
java::Clipboard::StopTrackingClipboardData(
java::GeckoAppShell::GetApplicationContext());
}
// static
nsresult nsClipboard::GetTextFromTransferable(nsITransferable* aTransferable,
nsString& aText,
nsString& aHTML) {
nsTArray<nsCString> flavors;
nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
if (NS_FAILED(rv)) {
return rv;
}
for (auto& flavorStr : flavors) {
if (flavorStr.EqualsLiteral(kTextMime)) {
nsCOMPtr<nsISupports> item;
nsresult rv =
aTransferable->GetTransferData(kTextMime, getter_AddRefs(item));
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(item);
if (supportsString) {
supportsString->GetData(aText);
}
} else if (flavorStr.EqualsLiteral(kHTMLMime)) {
nsCOMPtr<nsISupports> item;
nsresult rv =
aTransferable->GetTransferData(kHTMLMime, getter_AddRefs(item));
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(item);
if (supportsString) {
supportsString->GetData(aHTML);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) {
MOZ_DIAGNOSTIC_ASSERT(aTransferable);
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
if (!jni::IsAvailable()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsString text;
nsString html;
nsresult rv = GetTextFromTransferable(aTransferable, text, html);
if (NS_FAILED(rv)) {
return rv;
}
bool isPrivate = aTransferable->GetIsPrivateData();
if (!html.IsEmpty() &&
java::Clipboard::SetHTML(java::GeckoAppShell::GetApplicationContext(),
text, html, isPrivate)) {
return NS_OK;
}
if (!text.IsEmpty() &&
java::Clipboard::SetText(java::GeckoAppShell::GetApplicationContext(),
text, isPrivate)) {
return NS_OK;
}
return NS_ERROR_FAILURE;
}
mozilla::Result<nsCOMPtr<nsISupports>, nsresult>
nsClipboard::GetNativeClipboardData(const nsACString& aFlavor,
ClipboardType aWhichClipboard,
uint64_t aThreshold) {
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
if (!jni::IsAvailable()) {
return Err(NS_ERROR_NOT_AVAILABLE);
}
if (aFlavor.EqualsLiteral(kTextMime) || aFlavor.EqualsLiteral(kHTMLMime)) {
auto text = java::Clipboard::GetTextData(
java::GeckoAppShell::GetApplicationContext(), aFlavor);
if (!text) {
return nsCOMPtr<nsISupports>{};
}
nsAutoString buffer(text->ToString());
buffer.StripChar(char16_t(0));
if (buffer.IsEmpty()) {
return nsCOMPtr<nsISupports>{};
}
if (aThreshold && aFlavor.EqualsLiteral(kTextMime) &&
buffer.Length() * 2 > aThreshold) {
return mozilla::Err(NS_ERROR_CLIPBOARD_TOO_BIG);
}
nsCOMPtr<nsISupports> wrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
aFlavor, buffer.get(), buffer.Length() * 2, getter_AddRefs(wrapper));
return std::move(wrapper);
}
mozilla::jni::ByteArray::LocalRef bytes;
nsresult rv = java::Clipboard::GetRawData(aFlavor, &bytes);
if (NS_FAILED(rv) || !bytes) {
return nsCOMPtr<nsISupports>{};
}
nsCOMPtr<nsIInputStream> byteStream;
rv = NS_NewByteInputStream(getter_AddRefs(byteStream),
mozilla::Span(reinterpret_cast<const char*>(
bytes->GetElements().Elements()),
bytes->Length()),
NS_ASSIGNMENT_COPY);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nsCOMPtr<nsISupports>{};
}
return nsCOMPtr<nsISupports>(std::move(byteStream));
}
nsresult nsClipboard::EmptyNativeClipboardData(ClipboardType aWhichClipboard) {
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
if (!jni::IsAvailable()) {
return NS_ERROR_NOT_AVAILABLE;
}
java::Clipboard::Clear(java::GeckoAppShell::GetApplicationContext());
return NS_OK;
}
mozilla::Result<int32_t, nsresult>
nsClipboard::GetNativeClipboardSequenceNumber(ClipboardType aWhichClipboard) {
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
if (!jni::IsAvailable()) {
return Err(NS_ERROR_NOT_AVAILABLE);
}
return java::Clipboard::GetSequenceNumber(
java::GeckoAppShell::GetApplicationContext());
}
mozilla::Result<bool, nsresult>
nsClipboard::HasNativeClipboardDataMatchingFlavors(
const nsTArray<nsCString>& aFlavorList, ClipboardType aWhichClipboard) {
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
if (!jni::IsAvailable()) {
return Err(NS_ERROR_NOT_AVAILABLE);
}
for (auto& flavor : aFlavorList) {
if (java::Clipboard::HasData(java::GeckoAppShell::GetApplicationContext(),
NS_ConvertASCIItoUTF16(flavor))) {
return true;
}
}
return false;
}