forked from mirrors/gecko-dev
		
	MozReview-Commit-ID: HP4E3cADa8i --HG-- extra : rebase_source : caffa42f22f6c25d62d080aa6f65e5105ad263e9
		
			
				
	
	
		
			944 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			944 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
 | 
						|
 * 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 <android/log.h>
 | 
						|
#include <dlfcn.h>
 | 
						|
#include <math.h>
 | 
						|
#include <GLES2/gl2.h>
 | 
						|
 | 
						|
#include "mozilla/layers/CompositorBridgeChild.h"
 | 
						|
#include "mozilla/layers/CompositorBridgeParent.h"
 | 
						|
 | 
						|
#include "mozilla/Hal.h"
 | 
						|
#include "nsXULAppAPI.h"
 | 
						|
#include <prthread.h>
 | 
						|
#include "AndroidBridge.h"
 | 
						|
#include "AndroidBridgeUtilities.h"
 | 
						|
#include "nsAlertsUtils.h"
 | 
						|
#include "nsAppShell.h"
 | 
						|
#include "nsOSHelperAppService.h"
 | 
						|
#include "nsWindow.h"
 | 
						|
#include "mozilla/Preferences.h"
 | 
						|
#include "nsThreadUtils.h"
 | 
						|
#include "nsIThreadManager.h"
 | 
						|
#include "gfxPlatform.h"
 | 
						|
#include "gfxContext.h"
 | 
						|
#include "mozilla/gfx/2D.h"
 | 
						|
#include "gfxUtils.h"
 | 
						|
#include "nsPresContext.h"
 | 
						|
#include "nsIDocShell.h"
 | 
						|
#include "nsPIDOMWindow.h"
 | 
						|
#include "mozilla/dom/ScreenOrientation.h"
 | 
						|
#include "nsIDOMWindowUtils.h"
 | 
						|
#include "mozilla/ClearOnShutdown.h"
 | 
						|
#include "nsPrintfCString.h"
 | 
						|
#include "nsContentUtils.h"
 | 
						|
#include "nsIScriptError.h"
 | 
						|
#include "nsIHttpChannel.h"
 | 
						|
 | 
						|
#include "EventDispatcher.h"
 | 
						|
#include "MediaCodec.h"
 | 
						|
#include "SurfaceTexture.h"
 | 
						|
#include "GLContextProvider.h"
 | 
						|
 | 
						|
#include "mozilla/TimeStamp.h"
 | 
						|
#include "mozilla/UniquePtr.h"
 | 
						|
#include "mozilla/dom/ContentChild.h"
 | 
						|
#include "nsIObserverService.h"
 | 
						|
#include "nsISupportsPrimitives.h"
 | 
						|
#include "MediaPrefs.h"
 | 
						|
#include "WidgetUtils.h"
 | 
						|
 | 
						|
#include "FennecJNIWrappers.h"
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
using namespace mozilla::gfx;
 | 
						|
using namespace mozilla::java;
 | 
						|
 | 
						|
AndroidBridge* AndroidBridge::sBridge = nullptr;
 | 
						|
static jobject sGlobalContext = nullptr;
 | 
						|
nsDataHashtable<nsStringHashKey, nsString> AndroidBridge::sStoragePaths;
 | 
						|
 | 
						|
jmethodID AndroidBridge::GetMethodID(JNIEnv* env, jclass jClass,
 | 
						|
                              const char* methodName, const char* methodType)
 | 
						|
{
 | 
						|
   jmethodID methodID = env->GetMethodID(jClass, methodName, methodType);
 | 
						|
   if (!methodID) {
 | 
						|
       ALOG(">>> FATAL JNI ERROR! GetMethodID(methodName=\"%s\", "
 | 
						|
            "methodType=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?",
 | 
						|
            methodName, methodType);
 | 
						|
       env->ExceptionDescribe();
 | 
						|
       MOZ_CRASH();
 | 
						|
   }
 | 
						|
   return methodID;
 | 
						|
}
 | 
						|
 | 
						|
jmethodID AndroidBridge::GetStaticMethodID(JNIEnv* env, jclass jClass,
 | 
						|
                               const char* methodName, const char* methodType)
 | 
						|
{
 | 
						|
  jmethodID methodID = env->GetStaticMethodID(jClass, methodName, methodType);
 | 
						|
  if (!methodID) {
 | 
						|
      ALOG(">>> FATAL JNI ERROR! GetStaticMethodID(methodName=\"%s\", "
 | 
						|
           "methodType=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?",
 | 
						|
           methodName, methodType);
 | 
						|
      env->ExceptionDescribe();
 | 
						|
      MOZ_CRASH();
 | 
						|
  }
 | 
						|
  return methodID;
 | 
						|
}
 | 
						|
 | 
						|
jfieldID AndroidBridge::GetFieldID(JNIEnv* env, jclass jClass,
 | 
						|
                           const char* fieldName, const char* fieldType)
 | 
						|
{
 | 
						|
    jfieldID fieldID = env->GetFieldID(jClass, fieldName, fieldType);
 | 
						|
    if (!fieldID) {
 | 
						|
        ALOG(">>> FATAL JNI ERROR! GetFieldID(fieldName=\"%s\", "
 | 
						|
             "fieldType=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?",
 | 
						|
             fieldName, fieldType);
 | 
						|
        env->ExceptionDescribe();
 | 
						|
        MOZ_CRASH();
 | 
						|
    }
 | 
						|
    return fieldID;
 | 
						|
}
 | 
						|
 | 
						|
jfieldID AndroidBridge::GetStaticFieldID(JNIEnv* env, jclass jClass,
 | 
						|
                           const char* fieldName, const char* fieldType)
 | 
						|
{
 | 
						|
    jfieldID fieldID = env->GetStaticFieldID(jClass, fieldName, fieldType);
 | 
						|
    if (!fieldID) {
 | 
						|
        ALOG(">>> FATAL JNI ERROR! GetStaticFieldID(fieldName=\"%s\", "
 | 
						|
             "fieldType=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?",
 | 
						|
             fieldName, fieldType);
 | 
						|
        env->ExceptionDescribe();
 | 
						|
        MOZ_CRASH();
 | 
						|
    }
 | 
						|
    return fieldID;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
AndroidBridge::ConstructBridge()
 | 
						|
{
 | 
						|
    /* NSS hack -- bionic doesn't handle recursive unloads correctly,
 | 
						|
     * because library finalizer functions are called with the dynamic
 | 
						|
     * linker lock still held.  This results in a deadlock when trying
 | 
						|
     * to call dlclose() while we're already inside dlclose().
 | 
						|
     * Conveniently, NSS has an env var that can prevent it from unloading.
 | 
						|
     */
 | 
						|
    putenv(const_cast<char*>("NSS_DISABLE_UNLOAD=1"));
 | 
						|
 | 
						|
    MOZ_ASSERT(!sBridge);
 | 
						|
    sBridge = new AndroidBridge();
 | 
						|
 | 
						|
    MediaPrefs::GetSingleton();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
AndroidBridge::DeconstructBridge()
 | 
						|
{
 | 
						|
    if (sBridge) {
 | 
						|
        delete sBridge;
 | 
						|
        // AndroidBridge destruction requires sBridge to still be valid,
 | 
						|
        // so we set sBridge to nullptr after deleting it.
 | 
						|
        sBridge = nullptr;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
AndroidBridge::~AndroidBridge()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
AndroidBridge::AndroidBridge()
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("AndroidBridge::Init");
 | 
						|
 | 
						|
    JNIEnv* const jEnv = jni::GetGeckoThreadEnv();
 | 
						|
    AutoLocalJNIFrame jniFrame(jEnv);
 | 
						|
 | 
						|
    mMessageQueue = java::GeckoThread::MsgQueue();
 | 
						|
    auto msgQueueClass = jni::Class::LocalRef::Adopt(
 | 
						|
            jEnv, jEnv->GetObjectClass(mMessageQueue.Get()));
 | 
						|
    // mMessageQueueNext must not be null
 | 
						|
    mMessageQueueNext = GetMethodID(
 | 
						|
            jEnv, msgQueueClass.Get(), "next", "()Landroid/os/Message;");
 | 
						|
    // mMessageQueueMessages may be null (e.g. due to proguard optimization)
 | 
						|
    mMessageQueueMessages = jEnv->GetFieldID(
 | 
						|
            msgQueueClass.Get(), "mMessages", "Landroid/os/Message;");
 | 
						|
 | 
						|
    AutoJNIClass string(jEnv, "java/lang/String");
 | 
						|
    jStringClass = string.getGlobalRef();
 | 
						|
 | 
						|
    mAPIVersion = jni::GetAPIVersion();
 | 
						|
 | 
						|
    AutoJNIClass channels(jEnv, "java/nio/channels/Channels");
 | 
						|
    jChannels = channels.getGlobalRef();
 | 
						|
    jChannelCreate = channels.getStaticMethod("newChannel", "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
 | 
						|
 | 
						|
    AutoJNIClass readableByteChannel(jEnv, "java/nio/channels/ReadableByteChannel");
 | 
						|
    jReadableByteChannel = readableByteChannel.getGlobalRef();
 | 
						|
    jByteBufferRead = readableByteChannel.getMethod("read", "(Ljava/nio/ByteBuffer;)I");
 | 
						|
 | 
						|
    AutoJNIClass inputStream(jEnv, "java/io/InputStream");
 | 
						|
    jInputStream = inputStream.getGlobalRef();
 | 
						|
    jClose = inputStream.getMethod("close", "()V");
 | 
						|
    jAvailable = inputStream.getMethod("available", "()I");
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
getHandlersFromStringArray(JNIEnv *aJNIEnv, jni::ObjectArray::Param aArr, size_t aLen,
 | 
						|
                           nsIMutableArray *aHandlersArray,
 | 
						|
                           nsIHandlerApp **aDefaultApp,
 | 
						|
                           const nsAString& aAction = EmptyString(),
 | 
						|
                           const nsACString& aMimeType = EmptyCString())
 | 
						|
{
 | 
						|
    nsString empty = EmptyString();
 | 
						|
 | 
						|
    auto getNormalizedString = [] (jni::Object::Param obj) -> nsString {
 | 
						|
        nsString out;
 | 
						|
        if (!obj) {
 | 
						|
            out.SetIsVoid(true);
 | 
						|
        } else {
 | 
						|
            out.Assign(jni::String::Ref::From(obj)->ToString());
 | 
						|
        }
 | 
						|
        return out;
 | 
						|
    };
 | 
						|
 | 
						|
    for (size_t i = 0; i < aLen; i += 4) {
 | 
						|
        nsString name(getNormalizedString(aArr->GetElement(i)));
 | 
						|
        nsString isDefault(getNormalizedString(aArr->GetElement(i + 1)));
 | 
						|
        nsString packageName(getNormalizedString(aArr->GetElement(i + 2)));
 | 
						|
        nsString className(getNormalizedString(aArr->GetElement(i + 3)));
 | 
						|
 | 
						|
        nsIHandlerApp* app = nsOSHelperAppService::
 | 
						|
            CreateAndroidHandlerApp(name, className, packageName,
 | 
						|
                                    className, aMimeType, aAction);
 | 
						|
 | 
						|
        aHandlersArray->AppendElement(app);
 | 
						|
        if (aDefaultApp && isDefault.Length() > 0)
 | 
						|
            *aDefaultApp = app;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
AndroidBridge::GetHandlersForMimeType(const nsAString& aMimeType,
 | 
						|
                                      nsIMutableArray *aHandlersArray,
 | 
						|
                                      nsIHandlerApp **aDefaultApp,
 | 
						|
                                      const nsAString& aAction)
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("AndroidBridge::GetHandlersForMimeType");
 | 
						|
 | 
						|
    auto arr = GeckoAppShell::GetHandlersForMimeType(aMimeType, aAction);
 | 
						|
    if (!arr)
 | 
						|
        return false;
 | 
						|
 | 
						|
    JNIEnv* const env = arr.Env();
 | 
						|
    size_t len = arr->Length();
 | 
						|
 | 
						|
    if (!aHandlersArray)
 | 
						|
        return len > 0;
 | 
						|
 | 
						|
    getHandlersFromStringArray(env, arr, len, aHandlersArray,
 | 
						|
                               aDefaultApp, aAction,
 | 
						|
                               NS_ConvertUTF16toUTF8(aMimeType));
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
AndroidBridge::GetHWEncoderCapability()
 | 
						|
{
 | 
						|
  ALOG_BRIDGE("AndroidBridge::GetHWEncoderCapability");
 | 
						|
 | 
						|
  bool value = GeckoAppShell::GetHWEncoderCapability();
 | 
						|
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool
 | 
						|
AndroidBridge::GetHWDecoderCapability()
 | 
						|
{
 | 
						|
  ALOG_BRIDGE("AndroidBridge::GetHWDecoderCapability");
 | 
						|
 | 
						|
  bool value = GeckoAppShell::GetHWDecoderCapability();
 | 
						|
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
AndroidBridge::GetHandlersForURL(const nsAString& aURL,
 | 
						|
                                 nsIMutableArray* aHandlersArray,
 | 
						|
                                 nsIHandlerApp **aDefaultApp,
 | 
						|
                                 const nsAString& aAction)
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("AndroidBridge::GetHandlersForURL");
 | 
						|
 | 
						|
    auto arr = GeckoAppShell::GetHandlersForURL(aURL, aAction);
 | 
						|
    if (!arr)
 | 
						|
        return false;
 | 
						|
 | 
						|
    JNIEnv* const env = arr.Env();
 | 
						|
    size_t len = arr->Length();
 | 
						|
 | 
						|
    if (!aHandlersArray)
 | 
						|
        return len > 0;
 | 
						|
 | 
						|
    getHandlersFromStringArray(env, arr, len, aHandlersArray,
 | 
						|
                               aDefaultApp, aAction);
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
AndroidBridge::GetMimeTypeFromExtensions(const nsACString& aFileExt, nsCString& aMimeType)
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("AndroidBridge::GetMimeTypeFromExtensions");
 | 
						|
 | 
						|
    auto jstrType = GeckoAppShell::GetMimeTypeFromExtensions(aFileExt);
 | 
						|
 | 
						|
    if (jstrType) {
 | 
						|
        aMimeType = jstrType->ToCString();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
AndroidBridge::GetExtensionFromMimeType(const nsACString& aMimeType, nsACString& aFileExt)
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("AndroidBridge::GetExtensionFromMimeType");
 | 
						|
 | 
						|
    auto jstrExt = GeckoAppShell::GetExtensionFromMimeType(aMimeType);
 | 
						|
 | 
						|
    if (jstrExt) {
 | 
						|
        aFileExt = jstrExt->ToCString();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
AndroidBridge::GetClipboardText(nsAString& aText)
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("AndroidBridge::GetClipboardText");
 | 
						|
 | 
						|
    auto text = Clipboard::GetText(GeckoAppShell::GetApplicationContext());
 | 
						|
 | 
						|
    if (text) {
 | 
						|
        aText = text->ToString();
 | 
						|
    }
 | 
						|
    return !!text;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
AndroidBridge::GetDPI()
 | 
						|
{
 | 
						|
    static int sDPI = 0;
 | 
						|
    if (sDPI)
 | 
						|
        return sDPI;
 | 
						|
 | 
						|
    const int DEFAULT_DPI = 160;
 | 
						|
 | 
						|
    sDPI = GeckoAppShell::GetDpi();
 | 
						|
    if (!sDPI) {
 | 
						|
        return DEFAULT_DPI;
 | 
						|
    }
 | 
						|
 | 
						|
    return sDPI;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
AndroidBridge::GetScreenDepth()
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("%s", __PRETTY_FUNCTION__);
 | 
						|
 | 
						|
    static int sDepth = 0;
 | 
						|
    if (sDepth)
 | 
						|
        return sDepth;
 | 
						|
 | 
						|
    const int DEFAULT_DEPTH = 16;
 | 
						|
 | 
						|
    if (jni::IsAvailable()) {
 | 
						|
        sDepth = GeckoAppShell::GetScreenDepth();
 | 
						|
    }
 | 
						|
    if (!sDepth)
 | 
						|
        return DEFAULT_DEPTH;
 | 
						|
 | 
						|
    return sDepth;
 | 
						|
}
 | 
						|
void
 | 
						|
AndroidBridge::Vibrate(const nsTArray<uint32_t>& aPattern)
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("%s", __PRETTY_FUNCTION__);
 | 
						|
 | 
						|
    uint32_t len = aPattern.Length();
 | 
						|
    if (!len) {
 | 
						|
        ALOG_BRIDGE("  invalid 0-length array");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // It's clear if this worth special-casing, but it creates less
 | 
						|
    // java junk, so dodges the GC.
 | 
						|
    if (len == 1) {
 | 
						|
        jlong d = aPattern[0];
 | 
						|
        if (d < 0) {
 | 
						|
            ALOG_BRIDGE("  invalid vibration duration < 0");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        GeckoAppShell::Vibrate(d);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // First element of the array vibrate() expects is how long to wait
 | 
						|
    // *before* vibrating.  For us, this is always 0.
 | 
						|
 | 
						|
    JNIEnv* const env = jni::GetGeckoThreadEnv();
 | 
						|
    AutoLocalJNIFrame jniFrame(env, 1);
 | 
						|
 | 
						|
    jlongArray array = env->NewLongArray(len + 1);
 | 
						|
    if (!array) {
 | 
						|
        ALOG_BRIDGE("  failed to allocate array");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    jlong* elts = env->GetLongArrayElements(array, nullptr);
 | 
						|
    elts[0] = 0;
 | 
						|
    for (uint32_t i = 0; i < aPattern.Length(); ++i) {
 | 
						|
        jlong d = aPattern[i];
 | 
						|
        if (d < 0) {
 | 
						|
            ALOG_BRIDGE("  invalid vibration duration < 0");
 | 
						|
            env->ReleaseLongArrayElements(array, elts, JNI_ABORT);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        elts[i + 1] = d;
 | 
						|
    }
 | 
						|
    env->ReleaseLongArrayElements(array, elts, 0);
 | 
						|
 | 
						|
    GeckoAppShell::Vibrate(jni::LongArray::Ref::From(array), -1 /* don't repeat */);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
AndroidBridge::GetSystemColors(AndroidSystemColors *aColors)
 | 
						|
{
 | 
						|
 | 
						|
    NS_ASSERTION(aColors != nullptr, "AndroidBridge::GetSystemColors: aColors is null!");
 | 
						|
    if (!aColors)
 | 
						|
        return;
 | 
						|
 | 
						|
    auto arr = GeckoAppShell::GetSystemColors();
 | 
						|
    if (!arr)
 | 
						|
        return;
 | 
						|
 | 
						|
    JNIEnv* const env = arr.Env();
 | 
						|
    uint32_t len = static_cast<uint32_t>(env->GetArrayLength(arr.Get()));
 | 
						|
    jint *elements = env->GetIntArrayElements(arr.Get(), 0);
 | 
						|
 | 
						|
    uint32_t colorsCount = sizeof(AndroidSystemColors) / sizeof(nscolor);
 | 
						|
    if (len < colorsCount)
 | 
						|
        colorsCount = len;
 | 
						|
 | 
						|
    // Convert Android colors to nscolor by switching R and B in the ARGB 32 bit value
 | 
						|
    nscolor *colors = (nscolor*)aColors;
 | 
						|
 | 
						|
    for (uint32_t i = 0; i < colorsCount; i++) {
 | 
						|
        uint32_t androidColor = static_cast<uint32_t>(elements[i]);
 | 
						|
        uint8_t r = (androidColor & 0x00ff0000) >> 16;
 | 
						|
        uint8_t b = (androidColor & 0x000000ff);
 | 
						|
        colors[i] = (androidColor & 0xff00ff00) | (b << 16) | r;
 | 
						|
    }
 | 
						|
 | 
						|
    env->ReleaseIntArrayElements(arr.Get(), elements, 0);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
AndroidBridge::GetIconForExtension(const nsACString& aFileExt, uint32_t aIconSize, uint8_t * const aBuf)
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("AndroidBridge::GetIconForExtension");
 | 
						|
    NS_ASSERTION(aBuf != nullptr, "AndroidBridge::GetIconForExtension: aBuf is null!");
 | 
						|
    if (!aBuf)
 | 
						|
        return;
 | 
						|
 | 
						|
    auto arr = GeckoAppShell::GetIconForExtension(NS_ConvertUTF8toUTF16(aFileExt), aIconSize);
 | 
						|
 | 
						|
    NS_ASSERTION(arr != nullptr, "AndroidBridge::GetIconForExtension: Returned pixels array is null!");
 | 
						|
    if (!arr)
 | 
						|
        return;
 | 
						|
 | 
						|
    JNIEnv* const env = arr.Env();
 | 
						|
    uint32_t len = static_cast<uint32_t>(env->GetArrayLength(arr.Get()));
 | 
						|
    jbyte *elements = env->GetByteArrayElements(arr.Get(), 0);
 | 
						|
 | 
						|
    uint32_t bufSize = aIconSize * aIconSize * 4;
 | 
						|
    NS_ASSERTION(len == bufSize, "AndroidBridge::GetIconForExtension: Pixels array is incomplete!");
 | 
						|
    if (len == bufSize)
 | 
						|
        memcpy(aBuf, elements, bufSize);
 | 
						|
 | 
						|
    env->ReleaseByteArrayElements(arr.Get(), elements, 0);
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
AndroidBridge::GetStaticIntField(const char *className, const char *fieldName, int32_t* aInt, JNIEnv* jEnv /* = nullptr */)
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("AndroidBridge::GetStaticIntField %s", fieldName);
 | 
						|
 | 
						|
    if (!jEnv) {
 | 
						|
        if (!jni::IsAvailable()) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        jEnv = jni::GetGeckoThreadEnv();
 | 
						|
    }
 | 
						|
 | 
						|
    AutoJNIClass cls(jEnv, className);
 | 
						|
    jfieldID field = cls.getStaticField(fieldName, "I");
 | 
						|
 | 
						|
    if (!field) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    *aInt = static_cast<int32_t>(jEnv->GetStaticIntField(cls.getRawRef(), field));
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
AndroidBridge::GetStaticStringField(const char *className, const char *fieldName, nsAString &result, JNIEnv* jEnv /* = nullptr */)
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("AndroidBridge::GetStaticStringField %s", fieldName);
 | 
						|
 | 
						|
    if (!jEnv) {
 | 
						|
        if (!jni::IsAvailable()) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        jEnv = jni::GetGeckoThreadEnv();
 | 
						|
    }
 | 
						|
 | 
						|
    AutoLocalJNIFrame jniFrame(jEnv, 1);
 | 
						|
    AutoJNIClass cls(jEnv, className);
 | 
						|
    jfieldID field = cls.getStaticField(fieldName, "Ljava/lang/String;");
 | 
						|
 | 
						|
    if (!field) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    jstring jstr = (jstring) jEnv->GetStaticObjectField(cls.getRawRef(), field);
 | 
						|
    if (!jstr)
 | 
						|
        return false;
 | 
						|
 | 
						|
    result.Assign(jni::String::Ref::From(jstr)->ToString());
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
    class TracerRunnable : public Runnable{
 | 
						|
    public:
 | 
						|
        TracerRunnable() : Runnable("TracerRunnable") {
 | 
						|
            mTracerLock = new Mutex("TracerRunnable");
 | 
						|
            mTracerCondVar = new CondVar(*mTracerLock, "TracerRunnable");
 | 
						|
            mMainThread = do_GetMainThread();
 | 
						|
 | 
						|
        }
 | 
						|
        ~TracerRunnable() {
 | 
						|
            delete mTracerCondVar;
 | 
						|
            delete mTracerLock;
 | 
						|
            mTracerLock = nullptr;
 | 
						|
            mTracerCondVar = nullptr;
 | 
						|
        }
 | 
						|
 | 
						|
        virtual nsresult Run() {
 | 
						|
            MutexAutoLock lock(*mTracerLock);
 | 
						|
            if (!AndroidBridge::Bridge())
 | 
						|
                return NS_OK;
 | 
						|
 | 
						|
            mHasRun = true;
 | 
						|
            mTracerCondVar->Notify();
 | 
						|
            return NS_OK;
 | 
						|
        }
 | 
						|
 | 
						|
        bool Fire() {
 | 
						|
            if (!mTracerLock || !mTracerCondVar)
 | 
						|
                return false;
 | 
						|
            MutexAutoLock lock(*mTracerLock);
 | 
						|
            mHasRun = false;
 | 
						|
            mMainThread->Dispatch(this, NS_DISPATCH_NORMAL);
 | 
						|
            while (!mHasRun)
 | 
						|
                mTracerCondVar->Wait();
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        void Signal() {
 | 
						|
            MutexAutoLock lock(*mTracerLock);
 | 
						|
            mHasRun = true;
 | 
						|
            mTracerCondVar->Notify();
 | 
						|
        }
 | 
						|
    private:
 | 
						|
        Mutex* mTracerLock;
 | 
						|
        CondVar* mTracerCondVar;
 | 
						|
        bool mHasRun;
 | 
						|
        nsCOMPtr<nsIThread> mMainThread;
 | 
						|
 | 
						|
    };
 | 
						|
    StaticRefPtr<TracerRunnable> sTracerRunnable;
 | 
						|
 | 
						|
    bool InitWidgetTracing() {
 | 
						|
        if (!sTracerRunnable)
 | 
						|
            sTracerRunnable = new TracerRunnable();
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    void CleanUpWidgetTracing() {
 | 
						|
        sTracerRunnable = nullptr;
 | 
						|
    }
 | 
						|
 | 
						|
    bool FireAndWaitForTracerEvent() {
 | 
						|
        if (sTracerRunnable)
 | 
						|
            return sTracerRunnable->Fire();
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    void SignalTracerThread()
 | 
						|
    {
 | 
						|
        if (sTracerRunnable)
 | 
						|
            return sTracerRunnable->Signal();
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
AndroidBridge::GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("AndroidBridge::GetCurrentBatteryInformation");
 | 
						|
 | 
						|
    // To prevent calling too many methods through JNI, the Java method returns
 | 
						|
    // an array of double even if we actually want a double and a boolean.
 | 
						|
    auto arr = GeckoAppShell::GetCurrentBatteryInformation();
 | 
						|
 | 
						|
    JNIEnv* const env = arr.Env();
 | 
						|
    if (!arr || env->GetArrayLength(arr.Get()) != 3) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    jdouble* info = env->GetDoubleArrayElements(arr.Get(), 0);
 | 
						|
 | 
						|
    aBatteryInfo->level() = info[0];
 | 
						|
    aBatteryInfo->charging() = info[1] == 1.0f;
 | 
						|
    aBatteryInfo->remainingTime() = info[2];
 | 
						|
 | 
						|
    env->ReleaseDoubleArrayElements(arr.Get(), info, 0);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
AndroidBridge::GetCurrentNetworkInformation(hal::NetworkInformation* aNetworkInfo)
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("AndroidBridge::GetCurrentNetworkInformation");
 | 
						|
 | 
						|
    // To prevent calling too many methods through JNI, the Java method returns
 | 
						|
    // an array of double even if we actually want an integer, a boolean, and an integer.
 | 
						|
 | 
						|
    auto arr = GeckoAppShell::GetCurrentNetworkInformation();
 | 
						|
 | 
						|
    JNIEnv* const env = arr.Env();
 | 
						|
    if (!arr || env->GetArrayLength(arr.Get()) != 3) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    jdouble* info = env->GetDoubleArrayElements(arr.Get(), 0);
 | 
						|
 | 
						|
    aNetworkInfo->type() = info[0];
 | 
						|
    aNetworkInfo->isWifi() = info[1] == 1.0f;
 | 
						|
    aNetworkInfo->dhcpGateway() = info[2];
 | 
						|
 | 
						|
    env->ReleaseDoubleArrayElements(arr.Get(), info, 0);
 | 
						|
}
 | 
						|
 | 
						|
jobject
 | 
						|
AndroidBridge::GetGlobalContextRef() {
 | 
						|
    // The context object can change, so get a fresh copy every time.
 | 
						|
    auto context = GeckoAppShell::GetApplicationContext();
 | 
						|
    sGlobalContext = jni::Object::GlobalRef(context).Forget();
 | 
						|
    MOZ_ASSERT(sGlobalContext);
 | 
						|
    return sGlobalContext;
 | 
						|
}
 | 
						|
 | 
						|
/* Implementation file */
 | 
						|
NS_IMPL_ISUPPORTS(nsAndroidBridge,
 | 
						|
                  nsIAndroidEventDispatcher,
 | 
						|
                  nsIAndroidBridge,
 | 
						|
                  nsIObserver)
 | 
						|
 | 
						|
nsAndroidBridge::nsAndroidBridge() :
 | 
						|
    mAudibleWindowsNum(0)
 | 
						|
{
 | 
						|
  if (jni::IsAvailable()) {
 | 
						|
    RefPtr<widget::EventDispatcher> dispatcher = new widget::EventDispatcher();
 | 
						|
    dispatcher->Attach(java::EventDispatcher::GetInstance(),
 | 
						|
                       /* window */ nullptr);
 | 
						|
    mEventDispatcher = dispatcher;
 | 
						|
  }
 | 
						|
 | 
						|
  AddObservers();
 | 
						|
}
 | 
						|
 | 
						|
nsAndroidBridge::~nsAndroidBridge()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsAndroidBridge::HandleGeckoMessage(JS::HandleValue val,
 | 
						|
                                                  JSContext *cx)
 | 
						|
{
 | 
						|
    // Spit out a warning before sending the message.
 | 
						|
    nsContentUtils::ReportToConsoleNonLocalized(
 | 
						|
        NS_LITERAL_STRING("Use of handleGeckoMessage is deprecated. "
 | 
						|
                          "Please use EventDispatcher from Messaging.jsm."),
 | 
						|
        nsIScriptError::warningFlag,
 | 
						|
        NS_LITERAL_CSTRING("nsIAndroidBridge"),
 | 
						|
        nullptr);
 | 
						|
 | 
						|
    JS::RootedValue jsonVal(cx);
 | 
						|
 | 
						|
    if (val.isObject()) {
 | 
						|
        jsonVal = val;
 | 
						|
 | 
						|
    } else {
 | 
						|
        // Handle legacy JSON messages.
 | 
						|
        if (!val.isString()) {
 | 
						|
            return NS_ERROR_INVALID_ARG;
 | 
						|
        }
 | 
						|
        JS::RootedString jsonStr(cx, val.toString());
 | 
						|
 | 
						|
        if (!JS_ParseJSON(cx, jsonStr, &jsonVal) || !jsonVal.isObject()) {
 | 
						|
            JS_ClearPendingException(cx);
 | 
						|
            return NS_ERROR_INVALID_ARG;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    JS::RootedObject jsonObj(cx, &jsonVal.toObject());
 | 
						|
    JS::RootedValue typeVal(cx);
 | 
						|
 | 
						|
    if (!JS_GetProperty(cx, jsonObj, "type", &typeVal)) {
 | 
						|
        JS_ClearPendingException(cx);
 | 
						|
        return NS_ERROR_INVALID_ARG;
 | 
						|
    }
 | 
						|
 | 
						|
    return Dispatch(typeVal, jsonVal, /* callback */ nullptr, cx);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsAndroidBridge::ContentDocumentChanged(mozIDOMWindowProxy* aWindow)
 | 
						|
{
 | 
						|
    AndroidBridge::Bridge()->ContentDocumentChanged(aWindow);
 | 
						|
    return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsAndroidBridge::IsContentDocumentDisplayed(mozIDOMWindowProxy* aWindow,
 | 
						|
                                                          bool *aRet)
 | 
						|
{
 | 
						|
    *aRet = AndroidBridge::Bridge()->IsContentDocumentDisplayed(aWindow);
 | 
						|
    return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsAndroidBridge::Observe(nsISupports* aSubject, const char* aTopic,
 | 
						|
                         const char16_t* aData)
 | 
						|
{
 | 
						|
  if (!strcmp(aTopic, "xpcom-shutdown")) {
 | 
						|
    RemoveObservers();
 | 
						|
  } else if (!strcmp(aTopic, "audio-playback")) {
 | 
						|
    ALOG_BRIDGE("nsAndroidBridge::Observe, get audio-playback event.");
 | 
						|
 | 
						|
    nsAutoString activeStr(aData);
 | 
						|
    bool isPlaying = activeStr.EqualsLiteral("active");
 | 
						|
    UpdateAudioPlayingWindows(isPlaying);
 | 
						|
  }
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
nsAndroidBridge::UpdateAudioPlayingWindows(bool aPlaying)
 | 
						|
{
 | 
						|
  // Request audio focus for the first audio playing window and abandon focus
 | 
						|
  // for the last audio playing window.
 | 
						|
  MOZ_ASSERT(mAudibleWindowsNum >= 0);
 | 
						|
  if (aPlaying && mAudibleWindowsNum++ == 0) {
 | 
						|
    ALOG_BRIDGE("nsAndroidBridge, request audio focus.");
 | 
						|
    AudioFocusAgent::NotifyStartedPlaying();
 | 
						|
  } else if (!aPlaying && --mAudibleWindowsNum == 0) {
 | 
						|
    ALOG_BRIDGE("nsAndroidBridge, abandon audio focus.");
 | 
						|
    AudioFocusAgent::NotifyStoppedPlaying();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
nsAndroidBridge::AddObservers()
 | 
						|
{
 | 
						|
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 | 
						|
  if (obs) {
 | 
						|
    obs->AddObserver(this, "xpcom-shutdown", false);
 | 
						|
    if (jni::IsFennec()) { // No AudioFocusAgent in non-Fennec environment.
 | 
						|
        obs->AddObserver(this, "audio-playback", false);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
nsAndroidBridge::RemoveObservers()
 | 
						|
{
 | 
						|
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 | 
						|
  if (obs) {
 | 
						|
    obs->RemoveObserver(this, "xpcom-shutdown");
 | 
						|
    if (jni::IsFennec()) { // No AudioFocusAgent in non-Fennec environment.
 | 
						|
        obs->RemoveObserver(this, "audio-playback");
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
uint32_t
 | 
						|
AndroidBridge::GetScreenOrientation()
 | 
						|
{
 | 
						|
    ALOG_BRIDGE("AndroidBridge::GetScreenOrientation");
 | 
						|
 | 
						|
    int16_t orientation = GeckoAppShell::GetScreenOrientation();
 | 
						|
 | 
						|
    if (!orientation)
 | 
						|
        return dom::eScreenOrientation_None;
 | 
						|
 | 
						|
    return static_cast<dom::ScreenOrientationInternal>(orientation);
 | 
						|
}
 | 
						|
 | 
						|
uint16_t
 | 
						|
AndroidBridge::GetScreenAngle()
 | 
						|
{
 | 
						|
    return GeckoAppShell::GetScreenAngle();
 | 
						|
}
 | 
						|
 | 
						|
nsresult
 | 
						|
AndroidBridge::GetProxyForURI(const nsACString & aSpec,
 | 
						|
                              const nsACString & aScheme,
 | 
						|
                              const nsACString & aHost,
 | 
						|
                              const int32_t      aPort,
 | 
						|
                              nsACString & aResult)
 | 
						|
{
 | 
						|
    if (!jni::IsAvailable()) {
 | 
						|
        return NS_ERROR_FAILURE;
 | 
						|
    }
 | 
						|
 | 
						|
    auto jstrRet = GeckoAppShell::GetProxyForURI(aSpec, aScheme, aHost, aPort);
 | 
						|
 | 
						|
    if (!jstrRet)
 | 
						|
        return NS_ERROR_FAILURE;
 | 
						|
 | 
						|
    aResult = jstrRet->ToCString();
 | 
						|
    return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
AndroidBridge::PumpMessageLoop()
 | 
						|
{
 | 
						|
    JNIEnv* const env = jni::GetGeckoThreadEnv();
 | 
						|
 | 
						|
    if (mMessageQueueMessages) {
 | 
						|
        auto msg = jni::Object::LocalRef::Adopt(env,
 | 
						|
                env->GetObjectField(mMessageQueue.Get(),
 | 
						|
                                    mMessageQueueMessages));
 | 
						|
        // if queue.mMessages is null, queue.next() will block, which we don't
 | 
						|
        // want. It turns out to be an order of magnitude more performant to do
 | 
						|
        // this extra check here and block less vs. one fewer checks here and
 | 
						|
        // more blocking.
 | 
						|
        if (!msg) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    auto msg = jni::Object::LocalRef::Adopt(
 | 
						|
            env, env->CallObjectMethod(mMessageQueue.Get(), mMessageQueueNext));
 | 
						|
    if (!msg) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    return GeckoThread::PumpMessageLoop(msg);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsAndroidBridge::GetBrowserApp(nsIAndroidBrowserApp * *aBrowserApp)
 | 
						|
{
 | 
						|
    nsAppShell* const appShell = nsAppShell::Get();
 | 
						|
    if (appShell)
 | 
						|
        NS_IF_ADDREF(*aBrowserApp = appShell->GetBrowserApp());
 | 
						|
    return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP nsAndroidBridge::SetBrowserApp(nsIAndroidBrowserApp *aBrowserApp)
 | 
						|
{
 | 
						|
    nsAppShell* const appShell = nsAppShell::Get();
 | 
						|
    if (appShell)
 | 
						|
        appShell->SetBrowserApp(aBrowserApp);
 | 
						|
    return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
extern "C"
 | 
						|
__attribute__ ((visibility("default")))
 | 
						|
jobject JNICALL
 | 
						|
Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(JNIEnv *env, jclass, jlong size);
 | 
						|
 | 
						|
static RefPtr<nsWindow>
 | 
						|
GetWidget(mozIDOMWindowProxy* aWindow)
 | 
						|
{
 | 
						|
    MOZ_ASSERT(aWindow);
 | 
						|
 | 
						|
    nsCOMPtr<nsPIDOMWindowOuter> domWindow = nsPIDOMWindowOuter::From(aWindow);
 | 
						|
    nsCOMPtr<nsIWidget> widget =
 | 
						|
            widget::WidgetUtils::DOMWindowToWidget(domWindow);
 | 
						|
    MOZ_ASSERT(widget);
 | 
						|
 | 
						|
    return RefPtr<nsWindow>(static_cast<nsWindow*>(widget.get()));
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
AndroidBridge::ContentDocumentChanged(mozIDOMWindowProxy* aWindow)
 | 
						|
{
 | 
						|
    auto widget = GetWidget(aWindow);
 | 
						|
    if (widget) {
 | 
						|
        widget->SetContentDocumentDisplayed(false);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
AndroidBridge::IsContentDocumentDisplayed(mozIDOMWindowProxy* aWindow)
 | 
						|
{
 | 
						|
    auto widget = GetWidget(aWindow);
 | 
						|
    if (widget) {
 | 
						|
        return widget->IsContentDocumentDisplayed();
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
jni::Object::LocalRef AndroidBridge::ChannelCreate(jni::Object::Param stream) {
 | 
						|
    JNIEnv* const env = jni::GetEnvForThread();
 | 
						|
    auto rv = jni::Object::LocalRef::Adopt(env, env->CallStaticObjectMethod(
 | 
						|
            sBridge->jChannels, sBridge->jChannelCreate, stream.Get()));
 | 
						|
    MOZ_CATCH_JNI_EXCEPTION(env);
 | 
						|
    return rv;
 | 
						|
}
 | 
						|
 | 
						|
void AndroidBridge::InputStreamClose(jni::Object::Param obj) {
 | 
						|
    JNIEnv* const env = jni::GetEnvForThread();
 | 
						|
    env->CallVoidMethod(obj.Get(), sBridge->jClose);
 | 
						|
    MOZ_CATCH_JNI_EXCEPTION(env);
 | 
						|
}
 | 
						|
 | 
						|
uint32_t AndroidBridge::InputStreamAvailable(jni::Object::Param obj) {
 | 
						|
    JNIEnv* const env = jni::GetEnvForThread();
 | 
						|
    auto rv = env->CallIntMethod(obj.Get(), sBridge->jAvailable);
 | 
						|
    MOZ_CATCH_JNI_EXCEPTION(env);
 | 
						|
    return rv;
 | 
						|
}
 | 
						|
 | 
						|
nsresult AndroidBridge::InputStreamRead(jni::Object::Param obj, char *aBuf, uint32_t aCount, uint32_t *aRead) {
 | 
						|
    JNIEnv* const env = jni::GetEnvForThread();
 | 
						|
    auto arr = jni::ByteBuffer::New(aBuf, aCount);
 | 
						|
    jint read = env->CallIntMethod(obj.Get(), sBridge->jByteBufferRead, arr.Get());
 | 
						|
 | 
						|
    if (env->ExceptionCheck()) {
 | 
						|
        env->ExceptionClear();
 | 
						|
        return NS_ERROR_FAILURE;
 | 
						|
    }
 | 
						|
 | 
						|
    if (read <= 0) {
 | 
						|
        *aRead = 0;
 | 
						|
        return NS_OK;
 | 
						|
    }
 | 
						|
    *aRead = read;
 | 
						|
    return NS_OK;
 | 
						|
}
 |