bug 590349 - Clipboard (copy/paste) support for Android r=mwu a=blocking-fennec=2.0+
authorBrad Lassey <blassey@mozilla.com>
Thu, 26 Aug 2010 19:43:23 -0400
changeset 51539 6f3b20dd902a69cdc1165eb68dcf696b38837d55
parent 51538 2a539b9317e2ba6b5e97a56e96155150b270b2f4
child 51540 bf887b0ed6f530fe7799082d4e0df2ae4d62a623
push id15347
push userblassey@mozilla.com
push dateThu, 26 Aug 2010 23:43:59 +0000
treeherdermozilla-central@6f3b20dd902a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmwu, blocking-fennec
bugs590349
milestone2.0b5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 590349 - Clipboard (copy/paste) support for Android r=mwu a=blocking-fennec=2.0+
embedding/android/GeckoAppShell.java
widget/src/android/AndroidBridge.cpp
widget/src/android/AndroidBridge.h
widget/src/android/Makefile.in
widget/src/android/nsClipboard.cpp
widget/src/android/nsClipboard.h
widget/src/android/nsWidgetFactory.cpp
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -418,9 +418,25 @@ class GeckoAppShell
         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
         try {
             GeckoApp.surfaceView.getContext().startActivity(intent);
             return true;
         } catch(ActivityNotFoundException e) {
             return false;
         }
     }
+
+    static String getClipboardText() {
+        Context context = GeckoApp.surfaceView.getContext();
+        ClipboardManager cm = (ClipboardManager)
+            context.getSystemService(Context.CLIPBOARD_SERVICE);
+        if (!cm.hasText())
+            return null;
+        return cm.getText().toString();
+    }
+
+    static void setClipboardText(String text) {
+        Context context = GeckoApp.surfaceView.getContext();
+        ClipboardManager cm = (ClipboardManager)
+            context.getSystemService(Context.CLIPBOARD_SERVICE);
+        cm.setText(text);
+    }
 }
--- a/widget/src/android/AndroidBridge.cpp
+++ b/widget/src/android/AndroidBridge.cpp
@@ -103,16 +103,18 @@ AndroidBridge::Init(JNIEnv *jEnv,
     jReturnIMEQueryResult = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "returnIMEQueryResult", "(Ljava/lang/String;II)V");
     jScheduleRestart = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "scheduleRestart", "()V");
     jNotifyXreExit = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "onXreExit", "()V");
     jGetHandlersForMimeType = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getHandlersForMimeType", "(Ljava/lang/String;)[Ljava/lang/String;");
     jGetHandlersForProtocol = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getHandlersForProtocol", "(Ljava/lang/String;)[Ljava/lang/String;");
     jOpenUriExternal = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "openUriExternal", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
     jGetMimeTypeFromExtension = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getMimeTypeFromExtension", "(Ljava/lang/String;)Ljava/lang/String;");
     jMoveTaskToBack = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "moveTaskToBack", "()V");
+    jGetClipboardText = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getClipboardText", "()Ljava/lang/String;");
+    jSetClipboardText = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setClipboardText", "(Ljava/lang/String;)V");
 
 
     jEGLContextClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGLContext"));
     jEGL10Class = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGL10"));
     jEGLSurfaceImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLSurfaceImpl"));
     jEGLContextImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLContextImpl"));
     jEGLConfigImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLConfigImpl"));
     jEGLDisplayImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLDisplayImpl"));
@@ -355,16 +357,57 @@ AndroidBridge::GetMimeTypeFromExtension(
 }
 
 void
 AndroidBridge::MoveTaskToBack()
 {
     mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jMoveTaskToBack);
 }
 
+bool
+AndroidBridge::GetClipboardText(nsAString& aText)
+{
+    jstring jstrType =  
+        static_cast<jstring>(mJNIEnv->
+                             CallStaticObjectMethod(mGeckoAppShellClass,
+                                                    jGetClipboardText));
+    if (!jstrType)
+        return PR_FALSE;
+    nsJNIString jniStr(jstrType);
+    aText.Assign(jniStr);
+    return PR_TRUE;
+}
+
+void
+AndroidBridge::SetClipboardText(const nsAString& aText)
+{
+    const PRUnichar* wText;
+    PRUint32 wTextLen = NS_StringGetData(aText, &wText);
+    jstring jstr = mJNIEnv->NewString(wText, wTextLen);
+    mJNIEnv->CallStaticObjectMethod(mGeckoAppShellClass, jSetClipboardText, jstr);
+}
+
+bool
+AndroidBridge::ClipboardHasText()
+{
+    jstring jstrType =  
+        static_cast<jstring>(mJNIEnv->
+                             CallStaticObjectMethod(mGeckoAppShellClass,
+                                                    jGetClipboardText));
+    if (!jstrType)
+        return PR_FALSE;
+    return PR_TRUE;
+}
+
+void
+AndroidBridge::EmptyClipboard()
+{
+    mJNIEnv->CallStaticObjectMethod(mGeckoAppShellClass, jSetClipboardText, nsnull);
+}
+
 void
 AndroidBridge::SetSurfaceView(jobject obj)
 {
     mSurfaceView.Init(obj);
 }
 
 void *
 AndroidBridge::CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoSurfaceView &sview)
--- a/widget/src/android/AndroidBridge.h
+++ b/widget/src/android/AndroidBridge.h
@@ -121,16 +121,24 @@ public:
     PRBool OpenUriExternal(const nsACString& aUriSpec, const nsACString& aMimeType, 
                            const nsAString& aPackageName = EmptyString(), 
                            const nsAString& aClassName = EmptyString());
 
     void GetMimeTypeFromExtension(const nsACString& aFileExt, nsCString& aMimeType);
 
     void MoveTaskToBack();
 
+    bool GetClipboardText(nsAString& aText);
+
+    void SetClipboardText(const nsAString& aText);
+    
+    void EmptyClipboard();
+
+    bool ClipboardHasText();
+
     struct AutoLocalJNIFrame {
         AutoLocalJNIFrame(int nEntries = 128) : mEntries(nEntries) {
             AndroidBridge::Bridge()->JNI()->PushLocalFrame(mEntries);
         }
         // Note! Calling Purge makes all previous local refs created in
         // the AutoLocalJNIFrame's scope INVALID; be sure that you locked down
         // any local refs that you need to keep around in global refs!
         void Purge() {
@@ -176,16 +184,18 @@ protected:
     jmethodID jNotifyXreExit;
     jmethodID jScheduleRestart;
     jmethodID jGetOutstandingDrawEvents;
     jmethodID jGetHandlersForMimeType;
     jmethodID jGetHandlersForProtocol;
     jmethodID jOpenUriExternal;
     jmethodID jGetMimeTypeFromExtension;
     jmethodID jMoveTaskToBack;
+    jmethodID jGetClipboardText;
+    jmethodID jSetClipboardText;
 
     // stuff we need for CallEglCreateWindowSurface
     jclass jEGLSurfaceImplClass;
     jclass jEGLContextImplClass;
     jclass jEGLConfigImplClass;
     jclass jEGLDisplayImplClass;
     jclass jEGLContextClass;
     jclass jEGL10Class;
--- a/widget/src/android/Makefile.in
+++ b/widget/src/android/Makefile.in
@@ -57,21 +57,21 @@ CPPSRCS	= \
 	nsToolkit.cpp \
 	AndroidJavaWrappers.cpp \
 	AndroidBridge.cpp \
 	AndroidJNI.cpp \
 	nsWindow.cpp \
 	nsLookAndFeel.cpp \
 	nsScreenManagerAndroid.cpp \
 	nsIdleServiceAndroid.cpp \
+	nsClipboard.cpp \
 	$(NULL)
 
 NOT_THERE_YET_CPPSRCS = \
 	nsQtKeyUtils.cpp \
-	nsClipboard.cpp \
 	nsBidiKeyboard.cpp \
 	nsDragService.cpp \
 	nsNativeThemeQt.cpp \
 	mozqwidget.cpp \
 	nsSound.cpp \
 	nsFilePicker.cpp \
 	$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsClipboard.cpp
@@ -0,0 +1,141 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brad Lassey <blassey@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsClipboard.h"
+#include "nsISupportsPrimitives.h"
+#include "AndroidBridge.h"
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS1(nsClipboard, nsIClipboard)
+
+/* The Android clipboard only supports text and doesn't support mime types
+ * so we assume all clipboard data is text/unicode for now. Documentation
+ * indicates that support for other data types is planned for future
+ * releases.
+ */
+
+nsClipboard::nsClipboard()
+{
+}
+
+NS_IMETHODIMP
+nsClipboard::SetData(nsITransferable *aTransferable,
+                     nsIClipboardOwner *anOwner, PRInt32 aWhichClipboard)
+{
+  if (aWhichClipboard != kGlobalClipboard)
+    return NS_ERROR_NOT_IMPLEMENTED;
+  if (!AndroidBridge::Bridge())
+    return NS_ERROR_NOT_IMPLEMENTED;
+
+  nsCOMPtr<nsISupports> tmp;
+  PRUint32 len;
+  nsresult rv  = aTransferable->GetTransferData(kUnicodeMime, getter_AddRefs(tmp),
+                                                &len);
+  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);
+  AndroidBridge::Bridge()->SetClipboardText(buffer);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClipboard::GetData(nsITransferable *aTransferable, PRInt32 aWhichClipboard)
+{
+  if (aWhichClipboard != kGlobalClipboard)
+    return NS_ERROR_NOT_IMPLEMENTED;
+  if (!AndroidBridge::Bridge())
+    return NS_ERROR_NOT_IMPLEMENTED;
+
+  nsAutoString buffer;
+  if (!AndroidBridge::Bridge()->GetClipboardText(buffer))
+    return NS_ERROR_UNEXPECTED;
+
+  nsresult rv;
+  nsCOMPtr<nsISupportsString> dataWrapper =
+    do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = dataWrapper->SetData(buffer);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // 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,
+                                      buffer.Length() * sizeof(PRUnichar));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClipboard::EmptyClipboard(PRInt32 aWhichClipboard)
+{
+  if (aWhichClipboard != kGlobalClipboard)
+    return NS_ERROR_NOT_IMPLEMENTED;
+  if (AndroidBridge::Bridge())
+    AndroidBridge::Bridge()->EmptyClipboard();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClipboard::HasDataMatchingFlavors(const char **aFlavorList,
+                                    PRUint32 aLength, PRInt32 aWhichClipboard,
+                                    PRBool *aHasText NS_OUTPARAM)
+{
+  *aHasText = PR_FALSE;
+  if (aWhichClipboard != kGlobalClipboard)
+    return NS_ERROR_NOT_IMPLEMENTED;
+  if (AndroidBridge::Bridge())
+    *aHasText = AndroidBridge::Bridge()->ClipboardHasText();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClipboard::SupportsSelectionClipboard(PRBool *aIsSupported NS_OUTPARAM)
+{
+  *aIsSupported = PR_FALSE;
+  return NS_OK;
+}
+
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsClipboard.h
@@ -0,0 +1,51 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brad Lassey <blassey@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef NS_CLIPBOARD_H
+#define NS_CLIPBOARD_H
+
+#include "nsIClipboard.h"
+
+class nsClipboard : public nsIClipboard
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICLIPBOARD
+
+  nsClipboard();
+};
+
+#endif
--- a/widget/src/android/nsWidgetFactory.cpp
+++ b/widget/src/android/nsWidgetFactory.cpp
@@ -44,50 +44,65 @@
 #include "nsToolkit.h"
 
 #include "nsWindow.h"
 #include "nsLookAndFeel.h"
 #include "nsAppShellSingleton.h"
 #include "nsScreenManagerAndroid.h"
 
 #include "nsIdleServiceAndroid.h"
+#include "nsClipboard.h"
+#include "nsClipboardHelper.h"
+#include "nsTransferable.h"
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsToolkit)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsLookAndFeel)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerAndroid)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsIdleServiceAndroid)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboard)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
 
 NS_DEFINE_NAMED_CID(NS_TOOLKIT_CID);
 NS_DEFINE_NAMED_CID(NS_APPSHELL_CID);
 NS_DEFINE_NAMED_CID(NS_WINDOW_CID);
 NS_DEFINE_NAMED_CID(NS_CHILD_CID);
 NS_DEFINE_NAMED_CID(NS_LOOKANDFEEL_CID);
 NS_DEFINE_NAMED_CID(NS_SCREENMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_IDLE_SERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_TRANSFERABLE_CID);
+NS_DEFINE_NAMED_CID(NS_CLIPBOARD_CID);
+NS_DEFINE_NAMED_CID(NS_CLIPBOARDHELPER_CID);
 
 static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
   { &kNS_WINDOW_CID, false, NULL, nsWindowConstructor },
   { &kNS_CHILD_CID, false, NULL, nsWindowConstructor },
   { &kNS_APPSHELL_CID, false, NULL, nsAppShellConstructor },
   { &kNS_TOOLKIT_CID, false, NULL, nsToolkitConstructor },
   { &kNS_LOOKANDFEEL_CID, false, NULL, nsLookAndFeelConstructor },
   { &kNS_SCREENMANAGER_CID, false, NULL, nsScreenManagerAndroidConstructor },
   { &kNS_IDLE_SERVICE_CID, false, NULL, nsIdleServiceAndroidConstructor },
+  { &kNS_TRANSFERABLE_CID, false, NULL, nsTransferableConstructor },
+  { &kNS_CLIPBOARD_CID, false, NULL, nsClipboardConstructor },
+  { &kNS_CLIPBOARDHELPER_CID, false, NULL, nsClipboardHelperConstructor },
   { NULL }
 };
 
 static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
   { "@mozilla.org/widgets/window/android;1", &kNS_WINDOW_CID },
   { "@mozilla.org/widgets/child_window/android;1", &kNS_CHILD_CID },
   { "@mozilla.org/widget/appshell/android;1", &kNS_APPSHELL_CID },
   { "@mozilla.org/widget/toolkit/android;1", &kNS_TOOLKIT_CID },
   { "@mozilla.org/widget/lookandfeel/android;1", &kNS_LOOKANDFEEL_CID },
   { "@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID },
   { "@mozilla.org/widget/idleservice;1", &kNS_IDLE_SERVICE_CID },
+  { "@mozilla.org/widget/transferable;1", &kNS_TRANSFERABLE_CID },
+  { "@mozilla.org/widget/clipboard;1", &kNS_CLIPBOARD_CID },
+  { "@mozilla.org/widget/clipboardhelper;1", &kNS_CLIPBOARDHELPER_CID },
   { NULL }
 };
 
 static void
 nsWidgetAndroidModuleDtor()
 {
     nsAppShellShutdown();
 }