mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 07:15:46 +00:00
6bddc0a55e
This patch removes the 'ScreenOrientationInternal' type from dom/base/ScreenOrientation.h and moves it into the HalScreenConfiguration.h header, renaming it simply to 'ScreenOrientation' in the process. This has several knock-off effects: - It allows files that needed ScreenOrientationInternal to include a much smaller header than before - It greatly reduces the number of headers pulled in when including Hal.h - It clarifies the role of the type. The 'Internal' part in the name had nothing to do with it being part of the implementation. The type was public and called that way only to avoid clashing with the 'ScreenOrientation' class. Since we moved it into a different namespace it can be renamed safely. - It allows a file that was manually re-declaring 'ScreenConfigurationInternal' type to use the original one - Finally this fixes a few files which were missing headers they actually required but that would still build because unified compilation put them into units that already had those headers thanks to ScreenConfiguration.h Differential Revision: https://phabricator.services.mozilla.com/D4458 --HG-- extra : moz-landing-system : lando
885 lines
26 KiB
C++
885 lines
26 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 "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 "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();
|
|
}
|
|
|
|
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::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::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 hal::eScreenOrientation_None;
|
|
|
|
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::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;
|
|
}
|