Bug 676268 - Part 1. Support text/html on Android clipboard backend r=geckoview-reviewers,snorp

Actually, we only support `text/unicode` mime type on Android clipboard backend.  But Android API 16+ supports `text/html`, so we should support this type since Chrome/Blink already supports it.

Differential Revision: https://phabricator.services.mozilla.com/D22659

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Makoto Kato 2019-03-11 15:35:54 +00:00
Родитель f934771efe
Коммит 61804ba4b3
5 изменённых файлов: 161 добавлений и 66 удалений

Просмотреть файл

@ -43,7 +43,6 @@ skip-if = toolkit == 'android'
skip-if = toolkit == 'android'
[test_bug410986.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug414526.html]
[test_bug417418.html]
skip-if = android_version == '18' # bug 1147989
@ -61,11 +60,9 @@ skip-if = toolkit == 'android'
[test_bug471722.html]
[test_bug478725.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug480647.html]
[test_bug480972.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug483651.html]
[test_bug484181.html]
skip-if = toolkit == 'android'
@ -77,10 +74,8 @@ skip-if = toolkit == 'android' # bug 1299578
[test_bug514156.html]
[test_bug520189.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug525389.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug537046.html]
[test_bug549262.html]
skip-if = toolkit == 'android'
@ -225,7 +220,6 @@ skip-if = os == 'android'
[test_bug1230473.html]
[test_bug1247483.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug1248128.html]
[test_bug1250010.html]
[test_bug1257363.html]
@ -235,7 +229,6 @@ skip-if = toolkit == 'android'
[test_bug1270235.html]
[test_bug1306532.html]
subsuite = clipboard
skip-if = toolkit == 'android'
[test_bug1310912.html]
skip-if = toolkit == 'android' # bug 1315898
[test_bug1314790.html]

Просмотреть файл

@ -8,37 +8,97 @@ import org.mozilla.gecko.annotation.WrapForJNI;
import android.content.ClipboardManager;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.text.TextUtils;
public final class Clipboard {
private final static String HTML_MIME = "text/html";
private final static String UNICODE_MIME = "text/unicode";
private final static String LOGTAG = "GeckoClipboard";
private Clipboard() {
}
@WrapForJNI(calledFrom = "gecko")
/**
* Get the text on the primary clip on Android clipboard
*
* @param context application context.
* @return a plain text string of clipboard data.
*/
public static String getText(final Context context) {
return getData(context, UNICODE_MIME);
}
/**
* Get the data on the primary clip on clipboard
*
* @param context application context
* @param mimeType the mime type we want. This supports text/html and text/unicode only.
* If other type, we do nothing.
* @return a string into clipboard.
*/
@WrapForJNI(calledFrom = "gecko")
public static String getData(final Context context, final String mimeType) {
final ClipboardManager cm = (ClipboardManager)
context.getSystemService(Context.CLIPBOARD_SERVICE);
if (cm.hasPrimaryClip()) {
ClipData clip = cm.getPrimaryClip();
if (clip != null && clip.getItemCount() > 0) {
ClipData.Item item = clip.getItemAt(0);
return item.coerceToText(context).toString();
if (clip == null || clip.getItemCount() == 0) {
return null;
}
ClipDescription description = clip.getDescription();
if (HTML_MIME.equals(mimeType) && description.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML)) {
CharSequence data = clip.getItemAt(0).getHtmlText();
if (data == null) {
return null;
}
return data.toString();
}
if (UNICODE_MIME.equals(mimeType)) {
return clip.getItemAt(0).coerceToText(context).toString();
}
}
return null;
}
/**
* Set plain text to clipboard
*
* @param context application context
* @param text a plain text to set to clipboard
*/
@WrapForJNI(calledFrom = "gecko")
public static void setText(final Context context, final CharSequence text) {
setData(context, ClipData.newPlainText("text", text));
}
/**
* Store HTML to clipboard
*
* @param context application context
* @param text a plain text to set to clipboard
* @param html a html text to set to clipboard
*/
@WrapForJNI(calledFrom = "gecko")
public static void setHTML(final Context context, final CharSequence text, final String htmlText) {
setData(context, ClipData.newHtmlText("html", text, htmlText));
}
/**
* Store {@link android.content.ClipData} to clipboard
*
* @param context application context
* @param clipData a {@link android.content.ClipData} to set to clipboard
*/
private static void setData(final Context context, ClipData clipData) {
// In API Level 11 and above, CLIPBOARD_SERVICE returns android.content.ClipboardManager,
// which is a subclass of android.text.ClipboardManager.
final ClipboardManager cm = (ClipboardManager)
context.getSystemService(Context.CLIPBOARD_SERVICE);
try {
cm.setPrimaryClip(ClipData.newPlainText("Text", text));
cm.setPrimaryClip(clipData);
} catch (NullPointerException e) {
// Bug 776223: This is a Samsung clipboard bug. setPrimaryClip() can throw
// a NullPointerException if Samsung's /data/clipboard directory is full.
@ -50,8 +110,11 @@ public final class Clipboard {
* @return true if the clipboard is nonempty, false otherwise.
*/
@WrapForJNI(calledFrom = "gecko")
public static boolean hasText(final Context context) {
return !TextUtils.isEmpty(getText(context));
public static boolean hasData(final Context context, final String mimeType) {
if (HTML_MIME.equals(mimeType) || UNICODE_MIME.equals(mimeType)) {
return !TextUtils.isEmpty(getData(context, mimeType));
}
return false;
}
/**

Просмотреть файл

@ -292,17 +292,6 @@ void AndroidBridge::GetExtensionFromMimeType(const nsACString& aMimeType,
}
}
bool AndroidBridge::GetClipboardText(nsAString& aText) {
ALOG_BRIDGE("AndroidBridge::GetClipboardText");
auto text = Clipboard::GetText(GeckoAppShell::GetApplicationContext());
if (text) {
aText = text->ToString();
}
return !!text;
}
int AndroidBridge::GetScreenDepth() {
ALOG_BRIDGE("%s", __PRETTY_FUNCTION__);

Просмотреть файл

@ -113,8 +113,6 @@ class AndroidBridge final {
void GetExtensionFromMimeType(const nsACString& aMimeType,
nsACString& aFileExt);
bool GetClipboardText(nsAString& aText);
int GetScreenDepth();
void Vibrate(const nsTArray<uint32_t>& aPattern);

Просмотреть файл

@ -2,16 +2,14 @@
* 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/dom/ContentChild.h"
#include "nsClipboard.h"
#include "FennecJNIWrappers.h"
#include "nsISupportsPrimitives.h"
#include "AndroidBridge.h"
#include "nsCOMPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsXULAppAPI.h"
#include "nsPrimitiveHelpers.h"
using namespace mozilla;
using mozilla::dom::ContentChild;
NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard)
@ -28,52 +26,100 @@ nsClipboard::SetData(nsITransferable *aTransferable, nsIClipboardOwner *anOwner,
int32_t aWhichClipboard) {
if (aWhichClipboard != kGlobalClipboard) return NS_ERROR_NOT_IMPLEMENTED;
nsCOMPtr<nsISupports> tmp;
nsresult rv =
aTransferable->GetTransferData(kUnicodeMime, getter_AddRefs(tmp));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(tmp);
// No support for non-text data
NS_ENSURE_TRUE(supportsString, NS_ERROR_NOT_IMPLEMENTED);
nsAutoString buffer;
supportsString->GetData(buffer);
if (!jni::IsAvailable()) {
return NS_ERROR_NOT_AVAILABLE;
}
java::Clipboard::SetText(java::GeckoAppShell::GetApplicationContext(),
buffer);
return NS_OK;
nsTArray<nsCString> flavors;
aTransferable->FlavorsTransferableCanImport(flavors);
nsAutoString html;
nsAutoString text;
for (auto &flavorStr : flavors) {
if (flavorStr.EqualsLiteral(kUnicodeMime)) {
nsCOMPtr<nsISupports> item;
nsresult rv =
aTransferable->GetTransferData(kUnicodeMime, getter_AddRefs(item));
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(item);
if (supportsString) {
supportsString->GetData(text);
}
} 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(html);
}
}
}
if (!html.IsEmpty()) {
java::Clipboard::SetHTML(GeckoAppShell::GetApplicationContext(), text,
html);
return NS_OK;
}
if (!text.IsEmpty()) {
java::Clipboard::SetText(GeckoAppShell::GetApplicationContext(), text);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsClipboard::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard) {
if (aWhichClipboard != kGlobalClipboard) return NS_ERROR_NOT_IMPLEMENTED;
nsAutoString buffer;
if (!AndroidBridge::Bridge()) return NS_ERROR_NOT_IMPLEMENTED;
if (!AndroidBridge::Bridge()->GetClipboardText(buffer))
return NS_ERROR_UNEXPECTED;
if (!jni::IsAvailable()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv;
nsCOMPtr<nsISupportsString> dataWrapper =
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsTArray<nsCString> flavors;
aTransferable->FlavorsTransferableCanImport(flavors);
rv = dataWrapper->SetData(buffer);
NS_ENSURE_SUCCESS(rv, rv);
for (auto &flavorStr : flavors) {
if (flavorStr.EqualsLiteral(kUnicodeMime) ||
flavorStr.EqualsLiteral(kHTMLMime)) {
auto text =
Clipboard::GetData(GeckoAppShell::GetApplicationContext(), flavorStr);
if (!text) {
continue;
}
nsString buffer = text->ToString();
if (buffer.IsEmpty()) {
continue;
}
nsCOMPtr<nsISupports> wrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, buffer.get(),
buffer.Length() * 2,
getter_AddRefs(wrapper));
if (wrapper) {
aTransferable->SetTransferData(flavorStr.get(), wrapper);
return NS_OK;
}
}
}
// If our data flavor has already been added, this will fail. But we don't
// care
aTransferable->AddDataFlavor(kUnicodeMime);
nsCOMPtr<nsISupports> nsisupportsDataWrapper = do_QueryInterface(dataWrapper);
rv = aTransferable->SetTransferData(kUnicodeMime, nsisupportsDataWrapper);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsClipboard::EmptyClipboard(int32_t aWhichClipboard) {
if (aWhichClipboard != kGlobalClipboard) return NS_ERROR_NOT_IMPLEMENTED;
if (!jni::IsAvailable()) {
return NS_ERROR_NOT_AVAILABLE;
}
java::Clipboard::ClearText(java::GeckoAppShell::GetApplicationContext());
return NS_OK;
@ -85,11 +131,17 @@ nsClipboard::HasDataMatchingFlavors(const char **aFlavorList, uint32_t aLength,
*aHasText = false;
if (aWhichClipboard != kGlobalClipboard) return NS_ERROR_NOT_IMPLEMENTED;
if (!jni::IsAvailable()) {
return NS_ERROR_NOT_AVAILABLE;
}
for (uint32_t k = 0; k < aLength; k++) {
if (strcmp(aFlavorList[k], kUnicodeMime) == 0) {
*aHasText = java::Clipboard::HasText(
java::GeckoAppShell::GetApplicationContext());
break;
bool hasData =
java::Clipboard::HasData(java::GeckoAppShell::GetApplicationContext(),
NS_ConvertASCIItoUTF16(aFlavorList[k]));
if (hasData) {
*aHasText = true;
return NS_OK;
}
}