forked from mirrors/gecko-dev
The inclusions were removed with the following very crude script and the
resulting breakage was fixed up by hand. The manual fixups did either
revert the changes done by the script, replace a generic header with a more
specific one or replace a header with a forward declaration.
find . -name "*.idl" | grep -v web-platform | grep -v third_party | while read path; do
interfaces=$(grep "^\(class\|interface\).*:.*" "$path" | cut -d' ' -f2)
if [ -n "$interfaces" ]; then
if [[ "$interfaces" == *$'\n'* ]]; then
regexp="\("
for i in $interfaces; do regexp="$regexp$i\|"; done
regexp="${regexp%%\\\|}\)"
else
regexp="$interfaces"
fi
interface=$(basename "$path")
rg -l "#include.*${interface%%.idl}.h" . | while read path2; do
hits=$(grep -v "#include.*${interface%%.idl}.h" "$path2" | grep -c "$regexp" )
if [ $hits -eq 0 ]; then
echo "Removing ${interface} from ${path2}"
grep -v "#include.*${interface%%.idl}.h" "$path2" > "$path2".tmp
mv -f "$path2".tmp "$path2"
fi
done
fi
done
Differential Revision: https://phabricator.services.mozilla.com/D55444
--HG--
extra : moz-landing-system : lando
783 lines
24 KiB
C++
783 lines
24 KiB
C++
/* -*- Mode: c++; c-basic-offset: 2; 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 "AndroidRect.h"
|
|
#include "nsAlertsUtils.h"
|
|
#include "nsAppShell.h"
|
|
#include "nsOSHelperAppService.h"
|
|
#include "nsWindow.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "gfxPlatform.h"
|
|
#include "gfxContext.h"
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "gfxUtils.h"
|
|
#include "nsPresContext.h"
|
|
#include "nsPIDOMWindow.h"
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "nsContentUtils.h"
|
|
|
|
#include "EventDispatcher.h"
|
|
#include "MediaCodec.h"
|
|
#include "SurfaceTexture.h"
|
|
#include "GLContextProvider.h"
|
|
|
|
#include "mozilla/TimeStamp.h"
|
|
#include "mozilla/UniquePtr.h"
|
|
#include "nsIObserverService.h"
|
|
#include "WidgetUtils.h"
|
|
|
|
#include "GeneratedJNIWrappers.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();
|
|
}
|
|
|
|
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::HasHWVP8Encoder() {
|
|
ALOG_BRIDGE("AndroidBridge::HasHWVP8Encoder");
|
|
|
|
bool value = GeckoAppShell::HasHWVP8Encoder();
|
|
|
|
return value;
|
|
}
|
|
|
|
bool AndroidBridge::HasHWVP8Decoder() {
|
|
ALOG_BRIDGE("AndroidBridge::HasHWVP8Decoder");
|
|
|
|
bool value = GeckoAppShell::HasHWVP8Decoder();
|
|
|
|
return value;
|
|
}
|
|
|
|
bool AndroidBridge::HasHWH264() {
|
|
ALOG_BRIDGE("AndroidBridge::HasHWH264");
|
|
|
|
return HardwareCodecCapabilityUtils::HasHWH264();
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
gfx::Rect AndroidBridge::getScreenSize() {
|
|
ALOG_BRIDGE("AndroidBridge::getScreenSize");
|
|
|
|
java::sdk::Rect::LocalRef screenrect = GeckoAppShell::GetScreenSize();
|
|
gfx::Rect screensize(screenrect->Left(), screenrect->Top(),
|
|
screenrect->Width(), screenrect->Height());
|
|
|
|
return screensize;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
} // namespace mozilla
|
|
|
|
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::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();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void nsAndroidBridge::AddObservers() {
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
obs->AddObserver(this, "xpcom-shutdown", false);
|
|
}
|
|
}
|
|
|
|
void nsAndroidBridge::RemoveObservers() {
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
obs->RemoveObserver(this, "xpcom-shutdown");
|
|
}
|
|
}
|
|
|
|
uint32_t AndroidBridge::GetScreenOrientation() {
|
|
ALOG_BRIDGE("AndroidBridge::GetScreenOrientation");
|
|
|
|
int16_t orientation = GeckoAppShell::GetScreenOrientation();
|
|
|
|
return static_cast<hal::ScreenOrientation>(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::GetIsFennec(bool* aIsFennec) {
|
|
*aIsFennec = false;
|
|
return NS_OK;
|
|
}
|
|
|
|
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);
|
|
|
|
void AndroidBridge::ContentDocumentChanged(mozIDOMWindowProxy* aWindow) {
|
|
if (RefPtr<nsWindow> widget =
|
|
nsWindow::From(nsPIDOMWindowOuter::From(aWindow))) {
|
|
widget->SetContentDocumentDisplayed(false);
|
|
}
|
|
}
|
|
|
|
bool AndroidBridge::IsContentDocumentDisplayed(mozIDOMWindowProxy* aWindow) {
|
|
if (RefPtr<nsWindow> widget =
|
|
nsWindow::From(nsPIDOMWindowOuter::From(aWindow))) {
|
|
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;
|
|
}
|