|
1 /* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "mozilla/layers/CompositorChild.h" |
|
7 #include "mozilla/layers/CompositorParent.h" |
|
8 |
|
9 #include <android/log.h> |
|
10 #include <dlfcn.h> |
|
11 |
|
12 #include "mozilla/Hal.h" |
|
13 #include "nsXULAppAPI.h" |
|
14 #include <prthread.h> |
|
15 #include "nsXPCOMStrings.h" |
|
16 #include "AndroidBridge.h" |
|
17 #include "AndroidJNIWrapper.h" |
|
18 #include "AndroidBridgeUtilities.h" |
|
19 #include "nsAppShell.h" |
|
20 #include "nsOSHelperAppService.h" |
|
21 #include "nsWindow.h" |
|
22 #include "mozilla/Preferences.h" |
|
23 #include "nsThreadUtils.h" |
|
24 #include "nsIThreadManager.h" |
|
25 #include "mozilla/dom/mobilemessage/PSms.h" |
|
26 #include "gfxImageSurface.h" |
|
27 #include "gfxPlatform.h" |
|
28 #include "gfxContext.h" |
|
29 #include "mozilla/gfx/2D.h" |
|
30 #include "gfxUtils.h" |
|
31 #include "nsPresContext.h" |
|
32 #include "nsIDocShell.h" |
|
33 #include "nsPIDOMWindow.h" |
|
34 #include "mozilla/dom/ScreenOrientation.h" |
|
35 #include "nsIDOMWindowUtils.h" |
|
36 #include "nsIDOMClientRect.h" |
|
37 #include "StrongPointer.h" |
|
38 #include "mozilla/ClearOnShutdown.h" |
|
39 #include "nsPrintfCString.h" |
|
40 #include "NativeJSContainer.h" |
|
41 #include "nsContentUtils.h" |
|
42 #include "nsIScriptError.h" |
|
43 |
|
44 using namespace mozilla; |
|
45 using namespace mozilla::widget::android; |
|
46 using namespace mozilla::gfx; |
|
47 |
|
48 StaticRefPtr<AndroidBridge> AndroidBridge::sBridge; |
|
49 static unsigned sJavaEnvThreadIndex = 0; |
|
50 static jobject sGlobalContext = nullptr; |
|
51 static void JavaThreadDetachFunc(void *arg); |
|
52 |
|
53 // This is a dummy class that can be used in the template for android::sp |
|
54 class AndroidRefable { |
|
55 void incStrong(void* thing) { } |
|
56 void decStrong(void* thing) { } |
|
57 }; |
|
58 |
|
59 // This isn't in AndroidBridge.h because including StrongPointer.h there is gross |
|
60 static android::sp<AndroidRefable> (*android_SurfaceTexture_getNativeWindow)(JNIEnv* env, jobject surfaceTexture) = nullptr; |
|
61 |
|
62 jclass AndroidBridge::GetClassGlobalRef(JNIEnv* env, const char* className) |
|
63 { |
|
64 jobject classLocalRef = env->FindClass(className); |
|
65 if (!classLocalRef) { |
|
66 ALOG(">>> FATAL JNI ERROR! FindClass(className=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?", |
|
67 className); |
|
68 env->ExceptionDescribe(); |
|
69 MOZ_CRASH(); |
|
70 } |
|
71 jobject classGlobalRef = env->NewGlobalRef(classLocalRef); |
|
72 if (!classGlobalRef) { |
|
73 env->ExceptionDescribe(); |
|
74 MOZ_CRASH(); |
|
75 } |
|
76 // Local ref no longer necessary because we have a global ref. |
|
77 env->DeleteLocalRef(classLocalRef); |
|
78 classLocalRef = nullptr; |
|
79 return static_cast<jclass>(classGlobalRef); |
|
80 } |
|
81 |
|
82 jmethodID AndroidBridge::GetMethodID(JNIEnv* env, jclass jClass, |
|
83 const char* methodName, const char* methodType) |
|
84 { |
|
85 jmethodID methodID = env->GetMethodID(jClass, methodName, methodType); |
|
86 if (!methodID) { |
|
87 ALOG(">>> FATAL JNI ERROR! GetMethodID(methodName=\"%s\", " |
|
88 "methodType=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?", |
|
89 methodName, methodType); |
|
90 env->ExceptionDescribe(); |
|
91 MOZ_CRASH(); |
|
92 } |
|
93 return methodID; |
|
94 } |
|
95 |
|
96 jmethodID AndroidBridge::GetStaticMethodID(JNIEnv* env, jclass jClass, |
|
97 const char* methodName, const char* methodType) |
|
98 { |
|
99 jmethodID methodID = env->GetStaticMethodID(jClass, methodName, methodType); |
|
100 if (!methodID) { |
|
101 ALOG(">>> FATAL JNI ERROR! GetStaticMethodID(methodName=\"%s\", " |
|
102 "methodType=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?", |
|
103 methodName, methodType); |
|
104 env->ExceptionDescribe(); |
|
105 MOZ_CRASH(); |
|
106 } |
|
107 return methodID; |
|
108 } |
|
109 |
|
110 jfieldID AndroidBridge::GetFieldID(JNIEnv* env, jclass jClass, |
|
111 const char* fieldName, const char* fieldType) |
|
112 { |
|
113 jfieldID fieldID = env->GetFieldID(jClass, fieldName, fieldType); |
|
114 if (!fieldID) { |
|
115 ALOG(">>> FATAL JNI ERROR! GetFieldID(fieldName=\"%s\", " |
|
116 "fieldType=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?", |
|
117 fieldName, fieldType); |
|
118 env->ExceptionDescribe(); |
|
119 MOZ_CRASH(); |
|
120 } |
|
121 return fieldID; |
|
122 } |
|
123 |
|
124 jfieldID AndroidBridge::GetStaticFieldID(JNIEnv* env, jclass jClass, |
|
125 const char* fieldName, const char* fieldType) |
|
126 { |
|
127 jfieldID fieldID = env->GetStaticFieldID(jClass, fieldName, fieldType); |
|
128 if (!fieldID) { |
|
129 ALOG(">>> FATAL JNI ERROR! GetStaticFieldID(fieldName=\"%s\", " |
|
130 "fieldType=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?", |
|
131 fieldName, fieldType); |
|
132 env->ExceptionDescribe(); |
|
133 MOZ_CRASH(); |
|
134 } |
|
135 return fieldID; |
|
136 } |
|
137 |
|
138 void |
|
139 AndroidBridge::ConstructBridge(JNIEnv *jEnv) |
|
140 { |
|
141 /* NSS hack -- bionic doesn't handle recursive unloads correctly, |
|
142 * because library finalizer functions are called with the dynamic |
|
143 * linker lock still held. This results in a deadlock when trying |
|
144 * to call dlclose() while we're already inside dlclose(). |
|
145 * Conveniently, NSS has an env var that can prevent it from unloading. |
|
146 */ |
|
147 putenv("NSS_DISABLE_UNLOAD=1"); |
|
148 |
|
149 PR_NewThreadPrivateIndex(&sJavaEnvThreadIndex, JavaThreadDetachFunc); |
|
150 |
|
151 AndroidBridge *bridge = new AndroidBridge(); |
|
152 if (!bridge->Init(jEnv)) { |
|
153 delete bridge; |
|
154 } |
|
155 sBridge = bridge; |
|
156 } |
|
157 |
|
158 bool |
|
159 AndroidBridge::Init(JNIEnv *jEnv) |
|
160 { |
|
161 ALOG_BRIDGE("AndroidBridge::Init"); |
|
162 jEnv->GetJavaVM(&mJavaVM); |
|
163 if (!mJavaVM) { |
|
164 MOZ_CRASH(); // Nothing we can do here |
|
165 } |
|
166 |
|
167 AutoLocalJNIFrame jniFrame(jEnv); |
|
168 |
|
169 mJNIEnv = nullptr; |
|
170 mThread = -1; |
|
171 mGLControllerObj = nullptr; |
|
172 mOpenedGraphicsLibraries = false; |
|
173 mHasNativeBitmapAccess = false; |
|
174 mHasNativeWindowAccess = false; |
|
175 mHasNativeWindowFallback = false; |
|
176 |
|
177 initInit(); |
|
178 |
|
179 #ifdef MOZ_WEBSMS_BACKEND |
|
180 mAndroidSmsMessageClass = getClassGlobalRef("android/telephony/SmsMessage"); |
|
181 jCalculateLength = getStaticMethod("calculateLength", "(Ljava/lang/CharSequence;Z)[I"); |
|
182 #endif |
|
183 |
|
184 jStringClass = getClassGlobalRef("java/lang/String"); |
|
185 |
|
186 if (!GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &mAPIVersion, jEnv)) { |
|
187 ALOG_BRIDGE("Failed to find API version"); |
|
188 } |
|
189 |
|
190 jSurfaceClass = getClassGlobalRef("android/view/Surface"); |
|
191 if (mAPIVersion <= 8 /* Froyo */) { |
|
192 jSurfacePointerField = getField("mSurface", "I"); |
|
193 } else if (mAPIVersion > 8 && mAPIVersion < 19 /* KitKat */) { |
|
194 jSurfacePointerField = getField("mNativeSurface", "I"); |
|
195 } else { |
|
196 // We don't know how to get this, just set it to 0 |
|
197 jSurfacePointerField = 0; |
|
198 } |
|
199 |
|
200 jclass eglClass = getClassGlobalRef("com/google/android/gles_jni/EGLSurfaceImpl"); |
|
201 if (eglClass) { |
|
202 jEGLSurfacePointerField = getField("mEGLSurface", "I"); |
|
203 } else { |
|
204 jEGLSurfacePointerField = 0; |
|
205 } |
|
206 |
|
207 InitAndroidJavaWrappers(jEnv); |
|
208 |
|
209 // jEnv should NOT be cached here by anything -- the jEnv here |
|
210 // is not valid for the real gecko main thread, which is set |
|
211 // at SetMainThread time. |
|
212 |
|
213 return true; |
|
214 } |
|
215 |
|
216 bool |
|
217 AndroidBridge::SetMainThread(pthread_t thr) |
|
218 { |
|
219 ALOG_BRIDGE("AndroidBridge::SetMainThread"); |
|
220 if (thr) { |
|
221 mThread = thr; |
|
222 mJavaVM->GetEnv((void**) &mJNIEnv, JNI_VERSION_1_2); |
|
223 return (bool) mJNIEnv; |
|
224 } |
|
225 |
|
226 mJNIEnv = nullptr; |
|
227 mThread = -1; |
|
228 return true; |
|
229 } |
|
230 |
|
231 // Raw JNIEnv variants. |
|
232 jstring AndroidBridge::NewJavaString(JNIEnv* env, const char16_t* string, uint32_t len) { |
|
233 jstring ret = env->NewString(reinterpret_cast<const jchar*>(string), len); |
|
234 if (env->ExceptionCheck()) { |
|
235 ALOG_BRIDGE("Exceptional exit of: %s", __PRETTY_FUNCTION__); |
|
236 env->ExceptionDescribe(); |
|
237 env->ExceptionClear(); |
|
238 return nullptr; |
|
239 } |
|
240 return ret; |
|
241 } |
|
242 |
|
243 jstring AndroidBridge::NewJavaString(JNIEnv* env, const nsAString& string) { |
|
244 return NewJavaString(env, string.BeginReading(), string.Length()); |
|
245 } |
|
246 |
|
247 jstring AndroidBridge::NewJavaString(JNIEnv* env, const char* string) { |
|
248 return NewJavaString(env, NS_ConvertUTF8toUTF16(string)); |
|
249 } |
|
250 |
|
251 jstring AndroidBridge::NewJavaString(JNIEnv* env, const nsACString& string) { |
|
252 return NewJavaString(env, NS_ConvertUTF8toUTF16(string)); |
|
253 } |
|
254 |
|
255 // AutoLocalJNIFrame variants.. |
|
256 jstring AndroidBridge::NewJavaString(AutoLocalJNIFrame* frame, const char16_t* string, uint32_t len) { |
|
257 return NewJavaString(frame->GetEnv(), string, len); |
|
258 } |
|
259 |
|
260 jstring AndroidBridge::NewJavaString(AutoLocalJNIFrame* frame, const nsAString& string) { |
|
261 return NewJavaString(frame, string.BeginReading(), string.Length()); |
|
262 } |
|
263 |
|
264 jstring AndroidBridge::NewJavaString(AutoLocalJNIFrame* frame, const char* string) { |
|
265 return NewJavaString(frame, NS_ConvertUTF8toUTF16(string)); |
|
266 } |
|
267 |
|
268 jstring AndroidBridge::NewJavaString(AutoLocalJNIFrame* frame, const nsACString& string) { |
|
269 return NewJavaString(frame, NS_ConvertUTF8toUTF16(string)); |
|
270 } |
|
271 |
|
272 extern "C" { |
|
273 __attribute__ ((visibility("default"))) |
|
274 JNIEnv * GetJNIForThread() |
|
275 { |
|
276 JNIEnv *jEnv = static_cast<JNIEnv*>(PR_GetThreadPrivate(sJavaEnvThreadIndex)); |
|
277 if (jEnv) { |
|
278 return jEnv; |
|
279 } |
|
280 JavaVM *jVm = mozilla::AndroidBridge::GetVM(); |
|
281 if (!jVm->GetEnv(reinterpret_cast<void**>(&jEnv), JNI_VERSION_1_2)) { |
|
282 MOZ_ASSERT(jEnv); |
|
283 return jEnv; |
|
284 } |
|
285 if (!jVm->AttachCurrentThread(&jEnv, nullptr)) { |
|
286 MOZ_ASSERT(jEnv); |
|
287 PR_SetThreadPrivate(sJavaEnvThreadIndex, jEnv); |
|
288 return jEnv; |
|
289 } |
|
290 MOZ_CRASH(); |
|
291 return nullptr; // unreachable |
|
292 } |
|
293 } |
|
294 |
|
295 void AutoGlobalWrappedJavaObject::Dispose() { |
|
296 if (isNull()) { |
|
297 return; |
|
298 } |
|
299 |
|
300 GetJNIForThread()->DeleteGlobalRef(wrapped_obj); |
|
301 wrapped_obj = nullptr; |
|
302 } |
|
303 |
|
304 AutoGlobalWrappedJavaObject::~AutoGlobalWrappedJavaObject() { |
|
305 Dispose(); |
|
306 } |
|
307 |
|
308 static void |
|
309 getHandlersFromStringArray(JNIEnv *aJNIEnv, jobjectArray jArr, jsize aLen, |
|
310 nsIMutableArray *aHandlersArray, |
|
311 nsIHandlerApp **aDefaultApp, |
|
312 const nsAString& aAction = EmptyString(), |
|
313 const nsACString& aMimeType = EmptyCString()) |
|
314 { |
|
315 nsString empty = EmptyString(); |
|
316 for (jsize i = 0; i < aLen; i+=4) { |
|
317 |
|
318 AutoLocalJNIFrame jniFrame(aJNIEnv, 4); |
|
319 nsJNIString name( |
|
320 static_cast<jstring>(aJNIEnv->GetObjectArrayElement(jArr, i)), aJNIEnv); |
|
321 nsJNIString isDefault( |
|
322 static_cast<jstring>(aJNIEnv->GetObjectArrayElement(jArr, i + 1)), aJNIEnv); |
|
323 nsJNIString packageName( |
|
324 static_cast<jstring>(aJNIEnv->GetObjectArrayElement(jArr, i + 2)), aJNIEnv); |
|
325 nsJNIString className( |
|
326 static_cast<jstring>(aJNIEnv->GetObjectArrayElement(jArr, i + 3)), aJNIEnv); |
|
327 nsIHandlerApp* app = nsOSHelperAppService:: |
|
328 CreateAndroidHandlerApp(name, className, packageName, |
|
329 className, aMimeType, aAction); |
|
330 |
|
331 aHandlersArray->AppendElement(app, false); |
|
332 if (aDefaultApp && isDefault.Length() > 0) |
|
333 *aDefaultApp = app; |
|
334 } |
|
335 } |
|
336 |
|
337 bool |
|
338 AndroidBridge::GetHandlersForMimeType(const nsAString& aMimeType, |
|
339 nsIMutableArray *aHandlersArray, |
|
340 nsIHandlerApp **aDefaultApp, |
|
341 const nsAString& aAction) |
|
342 { |
|
343 ALOG_BRIDGE("AndroidBridge::GetHandlersForMimeType"); |
|
344 |
|
345 JNIEnv *env = GetJNIEnv(); |
|
346 |
|
347 AutoLocalJNIFrame jniFrame(env, 1); |
|
348 jobjectArray arr = |
|
349 mozilla::widget::android::GeckoAppShell::GetHandlersForMimeTypeWrapper(aMimeType, aAction); |
|
350 if (!arr) |
|
351 return false; |
|
352 |
|
353 jsize len = env->GetArrayLength(arr); |
|
354 |
|
355 if (!aHandlersArray) |
|
356 return len > 0; |
|
357 |
|
358 getHandlersFromStringArray(env, arr, len, aHandlersArray, |
|
359 aDefaultApp, aAction, |
|
360 NS_ConvertUTF16toUTF8(aMimeType)); |
|
361 return true; |
|
362 } |
|
363 |
|
364 bool |
|
365 AndroidBridge::GetHandlersForURL(const nsAString& aURL, |
|
366 nsIMutableArray* aHandlersArray, |
|
367 nsIHandlerApp **aDefaultApp, |
|
368 const nsAString& aAction) |
|
369 { |
|
370 ALOG_BRIDGE("AndroidBridge::GetHandlersForURL"); |
|
371 |
|
372 JNIEnv *env = GetJNIEnv(); |
|
373 |
|
374 AutoLocalJNIFrame jniFrame(env, 1); |
|
375 jobjectArray arr = mozilla::widget::android::GeckoAppShell::GetHandlersForURLWrapper(aURL, aAction); |
|
376 if (!arr) |
|
377 return false; |
|
378 |
|
379 jsize len = env->GetArrayLength(arr); |
|
380 |
|
381 if (!aHandlersArray) |
|
382 return len > 0; |
|
383 |
|
384 getHandlersFromStringArray(env, arr, len, aHandlersArray, |
|
385 aDefaultApp, aAction); |
|
386 return true; |
|
387 } |
|
388 |
|
389 void |
|
390 AndroidBridge::GetMimeTypeFromExtensions(const nsACString& aFileExt, nsCString& aMimeType) |
|
391 { |
|
392 ALOG_BRIDGE("AndroidBridge::GetMimeTypeFromExtensions"); |
|
393 |
|
394 JNIEnv *env = GetJNIEnv(); |
|
395 |
|
396 AutoLocalJNIFrame jniFrame(env, 1); |
|
397 jstring jstrType = mozilla::widget::android::GeckoAppShell::GetMimeTypeFromExtensionsWrapper |
|
398 (NS_ConvertUTF8toUTF16(aFileExt)); |
|
399 if (!jstrType) { |
|
400 return; |
|
401 } |
|
402 nsJNIString jniStr(jstrType, env); |
|
403 CopyUTF16toUTF8(jniStr.get(), aMimeType); |
|
404 } |
|
405 |
|
406 void |
|
407 AndroidBridge::GetExtensionFromMimeType(const nsACString& aMimeType, nsACString& aFileExt) |
|
408 { |
|
409 ALOG_BRIDGE("AndroidBridge::GetExtensionFromMimeType"); |
|
410 |
|
411 JNIEnv *env = GetJNIEnv(); |
|
412 |
|
413 AutoLocalJNIFrame jniFrame(env, 1); |
|
414 jstring jstrExt = mozilla::widget::android::GeckoAppShell::GetExtensionFromMimeTypeWrapper |
|
415 (NS_ConvertUTF8toUTF16(aMimeType)); |
|
416 if (!jstrExt) { |
|
417 return; |
|
418 } |
|
419 nsJNIString jniStr(jstrExt, env); |
|
420 CopyUTF16toUTF8(jniStr.get(), aFileExt); |
|
421 } |
|
422 |
|
423 bool |
|
424 AndroidBridge::GetClipboardText(nsAString& aText) |
|
425 { |
|
426 ALOG_BRIDGE("AndroidBridge::GetClipboardText"); |
|
427 |
|
428 JNIEnv *env = GetJNIEnv(); |
|
429 |
|
430 AutoLocalJNIFrame jniFrame(env, 1); |
|
431 jstring result = Clipboard::GetClipboardTextWrapper(); |
|
432 if (!result) |
|
433 return false; |
|
434 |
|
435 nsJNIString jniStr(result, env); |
|
436 aText.Assign(jniStr); |
|
437 return true; |
|
438 } |
|
439 |
|
440 void |
|
441 AndroidBridge::ShowAlertNotification(const nsAString& aImageUrl, |
|
442 const nsAString& aAlertTitle, |
|
443 const nsAString& aAlertText, |
|
444 const nsAString& aAlertCookie, |
|
445 nsIObserver *aAlertListener, |
|
446 const nsAString& aAlertName) |
|
447 { |
|
448 if (nsAppShell::gAppShell && aAlertListener) { |
|
449 // This will remove any observers already registered for this id |
|
450 nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeAddObserver(aAlertName, aAlertListener)); |
|
451 } |
|
452 |
|
453 mozilla::widget::android::GeckoAppShell::ShowAlertNotificationWrapper |
|
454 (aImageUrl, aAlertTitle, aAlertText, aAlertCookie, aAlertName); |
|
455 } |
|
456 |
|
457 int |
|
458 AndroidBridge::GetDPI() |
|
459 { |
|
460 static int sDPI = 0; |
|
461 if (sDPI) |
|
462 return sDPI; |
|
463 |
|
464 const int DEFAULT_DPI = 160; |
|
465 |
|
466 sDPI = mozilla::widget::android::GeckoAppShell::GetDpiWrapper(); |
|
467 if (!sDPI) { |
|
468 return DEFAULT_DPI; |
|
469 } |
|
470 |
|
471 return sDPI; |
|
472 } |
|
473 |
|
474 int |
|
475 AndroidBridge::GetScreenDepth() |
|
476 { |
|
477 ALOG_BRIDGE("%s", __PRETTY_FUNCTION__); |
|
478 |
|
479 static int sDepth = 0; |
|
480 if (sDepth) |
|
481 return sDepth; |
|
482 |
|
483 const int DEFAULT_DEPTH = 16; |
|
484 |
|
485 if (HasEnv()) { |
|
486 sDepth = mozilla::widget::android::GeckoAppShell::GetScreenDepthWrapper(); |
|
487 } |
|
488 if (!sDepth) |
|
489 return DEFAULT_DEPTH; |
|
490 |
|
491 return sDepth; |
|
492 } |
|
493 void |
|
494 AndroidBridge::Vibrate(const nsTArray<uint32_t>& aPattern) |
|
495 { |
|
496 ALOG_BRIDGE("%s", __PRETTY_FUNCTION__); |
|
497 |
|
498 uint32_t len = aPattern.Length(); |
|
499 if (!len) { |
|
500 ALOG_BRIDGE(" invalid 0-length array"); |
|
501 return; |
|
502 } |
|
503 |
|
504 JNIEnv *env = GetJNIEnv(); |
|
505 |
|
506 AutoLocalJNIFrame jniFrame(env, 1); |
|
507 |
|
508 // It's clear if this worth special-casing, but it creates less |
|
509 // java junk, so dodges the GC. |
|
510 if (len == 1) { |
|
511 jlong d = aPattern[0]; |
|
512 if (d < 0) { |
|
513 ALOG_BRIDGE(" invalid vibration duration < 0"); |
|
514 return; |
|
515 } |
|
516 mozilla::widget::android::GeckoAppShell::Vibrate1(d); |
|
517 return; |
|
518 } |
|
519 |
|
520 // First element of the array vibrate() expects is how long to wait |
|
521 // *before* vibrating. For us, this is always 0. |
|
522 |
|
523 jlongArray array = env->NewLongArray(len + 1); |
|
524 if (!array) { |
|
525 ALOG_BRIDGE(" failed to allocate array"); |
|
526 return; |
|
527 } |
|
528 |
|
529 jlong* elts = env->GetLongArrayElements(array, nullptr); |
|
530 elts[0] = 0; |
|
531 for (uint32_t i = 0; i < aPattern.Length(); ++i) { |
|
532 jlong d = aPattern[i]; |
|
533 if (d < 0) { |
|
534 ALOG_BRIDGE(" invalid vibration duration < 0"); |
|
535 env->ReleaseLongArrayElements(array, elts, JNI_ABORT); |
|
536 return; |
|
537 } |
|
538 elts[i + 1] = d; |
|
539 } |
|
540 env->ReleaseLongArrayElements(array, elts, 0); |
|
541 |
|
542 mozilla::widget::android::GeckoAppShell::VibrateA(array, -1/*don't repeat*/); |
|
543 } |
|
544 |
|
545 void |
|
546 AndroidBridge::GetSystemColors(AndroidSystemColors *aColors) |
|
547 { |
|
548 |
|
549 NS_ASSERTION(aColors != nullptr, "AndroidBridge::GetSystemColors: aColors is null!"); |
|
550 if (!aColors) |
|
551 return; |
|
552 |
|
553 JNIEnv *env = GetJNIEnv(); |
|
554 |
|
555 AutoLocalJNIFrame jniFrame(env, 1); |
|
556 |
|
557 jintArray arr = mozilla::widget::android::GeckoAppShell::GetSystemColoursWrapper(); |
|
558 if (!arr) |
|
559 return; |
|
560 |
|
561 uint32_t len = static_cast<uint32_t>(env->GetArrayLength(arr)); |
|
562 jint *elements = env->GetIntArrayElements(arr, 0); |
|
563 |
|
564 uint32_t colorsCount = sizeof(AndroidSystemColors) / sizeof(nscolor); |
|
565 if (len < colorsCount) |
|
566 colorsCount = len; |
|
567 |
|
568 // Convert Android colors to nscolor by switching R and B in the ARGB 32 bit value |
|
569 nscolor *colors = (nscolor*)aColors; |
|
570 |
|
571 for (uint32_t i = 0; i < colorsCount; i++) { |
|
572 uint32_t androidColor = static_cast<uint32_t>(elements[i]); |
|
573 uint8_t r = (androidColor & 0x00ff0000) >> 16; |
|
574 uint8_t b = (androidColor & 0x000000ff); |
|
575 colors[i] = (androidColor & 0xff00ff00) | (b << 16) | r; |
|
576 } |
|
577 |
|
578 env->ReleaseIntArrayElements(arr, elements, 0); |
|
579 } |
|
580 |
|
581 void |
|
582 AndroidBridge::GetIconForExtension(const nsACString& aFileExt, uint32_t aIconSize, uint8_t * const aBuf) |
|
583 { |
|
584 ALOG_BRIDGE("AndroidBridge::GetIconForExtension"); |
|
585 NS_ASSERTION(aBuf != nullptr, "AndroidBridge::GetIconForExtension: aBuf is null!"); |
|
586 if (!aBuf) |
|
587 return; |
|
588 |
|
589 JNIEnv *env = GetJNIEnv(); |
|
590 |
|
591 AutoLocalJNIFrame jniFrame(env, 1); |
|
592 |
|
593 jbyteArray arr = mozilla::widget::android::GeckoAppShell::GetIconForExtensionWrapper |
|
594 (NS_ConvertUTF8toUTF16(aFileExt), aIconSize); |
|
595 |
|
596 NS_ASSERTION(arr != nullptr, "AndroidBridge::GetIconForExtension: Returned pixels array is null!"); |
|
597 if (!arr) |
|
598 return; |
|
599 |
|
600 uint32_t len = static_cast<uint32_t>(env->GetArrayLength(arr)); |
|
601 jbyte *elements = env->GetByteArrayElements(arr, 0); |
|
602 |
|
603 uint32_t bufSize = aIconSize * aIconSize * 4; |
|
604 NS_ASSERTION(len == bufSize, "AndroidBridge::GetIconForExtension: Pixels array is incomplete!"); |
|
605 if (len == bufSize) |
|
606 memcpy(aBuf, elements, bufSize); |
|
607 |
|
608 env->ReleaseByteArrayElements(arr, elements, 0); |
|
609 } |
|
610 |
|
611 void |
|
612 AndroidBridge::SetLayerClient(JNIEnv* env, jobject jobj) |
|
613 { |
|
614 // if resetting is true, that means Android destroyed our GeckoApp activity |
|
615 // and we had to recreate it, but all the Gecko-side things were not destroyed. |
|
616 // We therefore need to link up the new java objects to Gecko, and that's what |
|
617 // we do here. |
|
618 bool resetting = (mLayerClient != nullptr); |
|
619 |
|
620 if (resetting) { |
|
621 // clear out the old layer client |
|
622 delete mLayerClient; |
|
623 mLayerClient = nullptr; |
|
624 } |
|
625 |
|
626 mLayerClient = mozilla::widget::android::GeckoLayerClient::Wrap(jobj); |
|
627 |
|
628 if (resetting) { |
|
629 // since we are re-linking the new java objects to Gecko, we need to get |
|
630 // the viewport from the compositor (since the Java copy was thrown away) |
|
631 // and we do that by setting the first-paint flag. |
|
632 nsWindow::ForceIsFirstPaint(); |
|
633 } |
|
634 } |
|
635 |
|
636 void |
|
637 AndroidBridge::RegisterCompositor(JNIEnv *env) |
|
638 { |
|
639 if (mGLControllerObj != nullptr && !mGLControllerObj->isNull()) { |
|
640 // we already have this set up, no need to do it again |
|
641 return; |
|
642 } |
|
643 |
|
644 jobject glController = LayerView::RegisterCompositorWrapper(); |
|
645 if (!glController) { |
|
646 return; |
|
647 } |
|
648 |
|
649 mGLControllerObj = GLController::Wrap(glController); |
|
650 } |
|
651 |
|
652 EGLSurface |
|
653 AndroidBridge::CreateEGLSurfaceForCompositor() |
|
654 { |
|
655 if (!jEGLSurfacePointerField) { |
|
656 return nullptr; |
|
657 } |
|
658 MOZ_ASSERT(mGLControllerObj, "AndroidBridge::CreateEGLSurfaceForCompositor called with a null GL controller ref"); |
|
659 |
|
660 JNIEnv* env = GetJNIForThread(); // called on the compositor thread |
|
661 |
|
662 AutoLocalJNIFrame jniFrame(env, 1); |
|
663 jobject eglSurface = mGLControllerObj->CreateEGLSurfaceForCompositorWrapper(); |
|
664 if (!eglSurface) |
|
665 return nullptr; |
|
666 |
|
667 EGLSurface ret = reinterpret_cast<EGLSurface>(env->GetIntField(eglSurface, jEGLSurfacePointerField)); |
|
668 return ret; |
|
669 } |
|
670 |
|
671 bool |
|
672 AndroidBridge::GetStaticIntField(const char *className, const char *fieldName, int32_t* aInt, JNIEnv* jEnv /* = nullptr */) |
|
673 { |
|
674 ALOG_BRIDGE("AndroidBridge::GetStaticIntField %s", fieldName); |
|
675 |
|
676 if (!jEnv) { |
|
677 if (!HasEnv()) { |
|
678 return false; |
|
679 } |
|
680 jEnv = GetJNIEnv(); |
|
681 } |
|
682 |
|
683 initInit(); |
|
684 getClassGlobalRef(className); |
|
685 jfieldID field = getStaticField(fieldName, "I"); |
|
686 |
|
687 if (!field) { |
|
688 jEnv->DeleteGlobalRef(jClass); |
|
689 return false; |
|
690 } |
|
691 |
|
692 *aInt = static_cast<int32_t>(jEnv->GetStaticIntField(jClass, field)); |
|
693 |
|
694 jEnv->DeleteGlobalRef(jClass); |
|
695 return true; |
|
696 } |
|
697 |
|
698 bool |
|
699 AndroidBridge::GetStaticStringField(const char *className, const char *fieldName, nsAString &result, JNIEnv* jEnv /* = nullptr */) |
|
700 { |
|
701 ALOG_BRIDGE("AndroidBridge::GetStaticStringField %s", fieldName); |
|
702 |
|
703 if (!jEnv) { |
|
704 if (!HasEnv()) { |
|
705 return false; |
|
706 } |
|
707 jEnv = GetJNIEnv(); |
|
708 } |
|
709 |
|
710 AutoLocalJNIFrame jniFrame(jEnv, 1); |
|
711 initInit(); |
|
712 getClassGlobalRef(className); |
|
713 jfieldID field = getStaticField(fieldName, "Ljava/lang/String;"); |
|
714 |
|
715 if (!field) { |
|
716 jEnv->DeleteGlobalRef(jClass); |
|
717 return false; |
|
718 } |
|
719 |
|
720 jstring jstr = (jstring) jEnv->GetStaticObjectField(jClass, field); |
|
721 jEnv->DeleteGlobalRef(jClass); |
|
722 if (!jstr) |
|
723 return false; |
|
724 |
|
725 result.Assign(nsJNIString(jstr, jEnv)); |
|
726 return true; |
|
727 } |
|
728 |
|
729 // Available for places elsewhere in the code to link to. |
|
730 bool |
|
731 mozilla_AndroidBridge_SetMainThread(pthread_t thr) |
|
732 { |
|
733 return AndroidBridge::Bridge()->SetMainThread(thr); |
|
734 } |
|
735 |
|
736 void* |
|
737 AndroidBridge::GetNativeSurface(JNIEnv* env, jobject surface) { |
|
738 if (!env || !mHasNativeWindowFallback || !jSurfacePointerField) |
|
739 return nullptr; |
|
740 |
|
741 return (void*)env->GetIntField(surface, jSurfacePointerField); |
|
742 } |
|
743 |
|
744 void |
|
745 AndroidBridge::OpenGraphicsLibraries() |
|
746 { |
|
747 if (!mOpenedGraphicsLibraries) { |
|
748 // Try to dlopen libjnigraphics.so for direct bitmap access on |
|
749 // Android 2.2+ (API level 8) |
|
750 mOpenedGraphicsLibraries = true; |
|
751 mHasNativeWindowAccess = false; |
|
752 mHasNativeWindowFallback = false; |
|
753 mHasNativeBitmapAccess = false; |
|
754 |
|
755 void *handle = dlopen("libjnigraphics.so", RTLD_LAZY | RTLD_LOCAL); |
|
756 if (handle) { |
|
757 AndroidBitmap_getInfo = (int (*)(JNIEnv *, jobject, void *))dlsym(handle, "AndroidBitmap_getInfo"); |
|
758 AndroidBitmap_lockPixels = (int (*)(JNIEnv *, jobject, void **))dlsym(handle, "AndroidBitmap_lockPixels"); |
|
759 AndroidBitmap_unlockPixels = (int (*)(JNIEnv *, jobject))dlsym(handle, "AndroidBitmap_unlockPixels"); |
|
760 |
|
761 mHasNativeBitmapAccess = AndroidBitmap_getInfo && AndroidBitmap_lockPixels && AndroidBitmap_unlockPixels; |
|
762 |
|
763 ALOG_BRIDGE("Successfully opened libjnigraphics.so, have native bitmap access? %d", mHasNativeBitmapAccess); |
|
764 } |
|
765 |
|
766 // Try to dlopen libandroid.so for and native window access on |
|
767 // Android 2.3+ (API level 9) |
|
768 handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL); |
|
769 if (handle) { |
|
770 ANativeWindow_fromSurface = (void* (*)(JNIEnv*, jobject))dlsym(handle, "ANativeWindow_fromSurface"); |
|
771 ANativeWindow_release = (void (*)(void*))dlsym(handle, "ANativeWindow_release"); |
|
772 ANativeWindow_setBuffersGeometry = (int (*)(void*, int, int, int)) dlsym(handle, "ANativeWindow_setBuffersGeometry"); |
|
773 ANativeWindow_lock = (int (*)(void*, void*, void*)) dlsym(handle, "ANativeWindow_lock"); |
|
774 ANativeWindow_unlockAndPost = (int (*)(void*))dlsym(handle, "ANativeWindow_unlockAndPost"); |
|
775 |
|
776 // This is only available in Honeycomb and ICS. It was removed in Jelly Bean |
|
777 ANativeWindow_fromSurfaceTexture = (void* (*)(JNIEnv*, jobject))dlsym(handle, "ANativeWindow_fromSurfaceTexture"); |
|
778 |
|
779 mHasNativeWindowAccess = ANativeWindow_fromSurface && ANativeWindow_release && ANativeWindow_lock && ANativeWindow_unlockAndPost; |
|
780 |
|
781 ALOG_BRIDGE("Successfully opened libandroid.so, have native window access? %d", mHasNativeWindowAccess); |
|
782 } |
|
783 |
|
784 // We need one symbol from here on Jelly Bean |
|
785 handle = dlopen("libandroid_runtime.so", RTLD_LAZY | RTLD_LOCAL); |
|
786 if (handle) { |
|
787 android_SurfaceTexture_getNativeWindow = (android::sp<AndroidRefable> (*)(JNIEnv*, jobject))dlsym(handle, "_ZN7android38android_SurfaceTexture_getNativeWindowEP7_JNIEnvP8_jobject"); |
|
788 } |
|
789 |
|
790 if (mHasNativeWindowAccess) |
|
791 return; |
|
792 |
|
793 // Look up Surface functions, used for native window (surface) fallback |
|
794 handle = dlopen("libsurfaceflinger_client.so", RTLD_LAZY); |
|
795 if (handle) { |
|
796 Surface_lock = (int (*)(void*, void*, void*, bool))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionEb"); |
|
797 Surface_unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv"); |
|
798 |
|
799 handle = dlopen("libui.so", RTLD_LAZY); |
|
800 if (handle) { |
|
801 Region_constructor = (void (*)(void*))dlsym(handle, "_ZN7android6RegionC1Ev"); |
|
802 Region_set = (void (*)(void*, void*))dlsym(handle, "_ZN7android6Region3setERKNS_4RectE"); |
|
803 |
|
804 mHasNativeWindowFallback = Surface_lock && Surface_unlockAndPost && Region_constructor && Region_set; |
|
805 } |
|
806 } |
|
807 } |
|
808 } |
|
809 |
|
810 namespace mozilla { |
|
811 class TracerRunnable : public nsRunnable{ |
|
812 public: |
|
813 TracerRunnable() { |
|
814 mTracerLock = new Mutex("TracerRunnable"); |
|
815 mTracerCondVar = new CondVar(*mTracerLock, "TracerRunnable"); |
|
816 mMainThread = do_GetMainThread(); |
|
817 |
|
818 } |
|
819 ~TracerRunnable() { |
|
820 delete mTracerCondVar; |
|
821 delete mTracerLock; |
|
822 mTracerLock = nullptr; |
|
823 mTracerCondVar = nullptr; |
|
824 } |
|
825 |
|
826 virtual nsresult Run() { |
|
827 MutexAutoLock lock(*mTracerLock); |
|
828 if (!AndroidBridge::Bridge()) |
|
829 return NS_OK; |
|
830 |
|
831 mHasRun = true; |
|
832 mTracerCondVar->Notify(); |
|
833 return NS_OK; |
|
834 } |
|
835 |
|
836 bool Fire() { |
|
837 if (!mTracerLock || !mTracerCondVar) |
|
838 return false; |
|
839 MutexAutoLock lock(*mTracerLock); |
|
840 mHasRun = false; |
|
841 mMainThread->Dispatch(this, NS_DISPATCH_NORMAL); |
|
842 while (!mHasRun) |
|
843 mTracerCondVar->Wait(); |
|
844 return true; |
|
845 } |
|
846 |
|
847 void Signal() { |
|
848 MutexAutoLock lock(*mTracerLock); |
|
849 mHasRun = true; |
|
850 mTracerCondVar->Notify(); |
|
851 } |
|
852 private: |
|
853 Mutex* mTracerLock; |
|
854 CondVar* mTracerCondVar; |
|
855 bool mHasRun; |
|
856 nsCOMPtr<nsIThread> mMainThread; |
|
857 |
|
858 }; |
|
859 StaticRefPtr<TracerRunnable> sTracerRunnable; |
|
860 |
|
861 bool InitWidgetTracing() { |
|
862 if (!sTracerRunnable) |
|
863 sTracerRunnable = new TracerRunnable(); |
|
864 return true; |
|
865 } |
|
866 |
|
867 void CleanUpWidgetTracing() { |
|
868 sTracerRunnable = nullptr; |
|
869 } |
|
870 |
|
871 bool FireAndWaitForTracerEvent() { |
|
872 if (sTracerRunnable) |
|
873 return sTracerRunnable->Fire(); |
|
874 return false; |
|
875 } |
|
876 |
|
877 void SignalTracerThread() |
|
878 { |
|
879 if (sTracerRunnable) |
|
880 return sTracerRunnable->Signal(); |
|
881 } |
|
882 |
|
883 } |
|
884 bool |
|
885 AndroidBridge::HasNativeBitmapAccess() |
|
886 { |
|
887 OpenGraphicsLibraries(); |
|
888 |
|
889 return mHasNativeBitmapAccess; |
|
890 } |
|
891 |
|
892 bool |
|
893 AndroidBridge::ValidateBitmap(jobject bitmap, int width, int height) |
|
894 { |
|
895 // This structure is defined in Android API level 8's <android/bitmap.h> |
|
896 // Because we can't depend on this, we get the function pointers via dlsym |
|
897 // and define this struct ourselves. |
|
898 struct BitmapInfo { |
|
899 uint32_t width; |
|
900 uint32_t height; |
|
901 uint32_t stride; |
|
902 uint32_t format; |
|
903 uint32_t flags; |
|
904 }; |
|
905 |
|
906 int err; |
|
907 struct BitmapInfo info = { 0, }; |
|
908 |
|
909 JNIEnv *env = GetJNIEnv(); |
|
910 |
|
911 if ((err = AndroidBitmap_getInfo(env, bitmap, &info)) != 0) { |
|
912 ALOG_BRIDGE("AndroidBitmap_getInfo failed! (error %d)", err); |
|
913 return false; |
|
914 } |
|
915 |
|
916 if ((int)info.width != width || (int)info.height != height) |
|
917 return false; |
|
918 |
|
919 return true; |
|
920 } |
|
921 |
|
922 bool |
|
923 AndroidBridge::InitCamera(const nsCString& contentType, uint32_t camera, uint32_t *width, uint32_t *height, uint32_t *fps) |
|
924 { |
|
925 JNIEnv *env = GetJNIEnv(); |
|
926 |
|
927 AutoLocalJNIFrame jniFrame(env, 1); |
|
928 jintArray arr = mozilla::widget::android::GeckoAppShell::InitCameraWrapper |
|
929 (NS_ConvertUTF8toUTF16(contentType), (int32_t) camera, (int32_t) width, (int32_t) height); |
|
930 |
|
931 if (!arr) |
|
932 return false; |
|
933 |
|
934 jint *elements = env->GetIntArrayElements(arr, 0); |
|
935 |
|
936 *width = elements[1]; |
|
937 *height = elements[2]; |
|
938 *fps = elements[3]; |
|
939 |
|
940 bool res = elements[0] == 1; |
|
941 |
|
942 env->ReleaseIntArrayElements(arr, elements, 0); |
|
943 |
|
944 return res; |
|
945 } |
|
946 |
|
947 void |
|
948 AndroidBridge::GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo) |
|
949 { |
|
950 ALOG_BRIDGE("AndroidBridge::GetCurrentBatteryInformation"); |
|
951 |
|
952 JNIEnv *env = GetJNIEnv(); |
|
953 |
|
954 AutoLocalJNIFrame jniFrame(env, 1); |
|
955 |
|
956 // To prevent calling too many methods through JNI, the Java method returns |
|
957 // an array of double even if we actually want a double and a boolean. |
|
958 jdoubleArray arr = mozilla::widget::android::GeckoAppShell::GetCurrentBatteryInformationWrapper(); |
|
959 if (!arr || env->GetArrayLength(arr) != 3) { |
|
960 return; |
|
961 } |
|
962 |
|
963 jdouble* info = env->GetDoubleArrayElements(arr, 0); |
|
964 |
|
965 aBatteryInfo->level() = info[0]; |
|
966 aBatteryInfo->charging() = info[1] == 1.0f; |
|
967 aBatteryInfo->remainingTime() = info[2]; |
|
968 |
|
969 env->ReleaseDoubleArrayElements(arr, info, 0); |
|
970 } |
|
971 |
|
972 void |
|
973 AndroidBridge::HandleGeckoMessage(JSContext* cx, JS::HandleObject object) |
|
974 { |
|
975 ALOG_BRIDGE("%s", __PRETTY_FUNCTION__); |
|
976 |
|
977 JNIEnv* const env = GetJNIEnv(); |
|
978 AutoLocalJNIFrame jniFrame(env, 1); |
|
979 const jobject message = |
|
980 mozilla::widget::CreateNativeJSContainer(env, cx, object); |
|
981 GeckoAppShell::HandleGeckoMessageWrapper(message); |
|
982 } |
|
983 |
|
984 nsresult |
|
985 AndroidBridge::GetSegmentInfoForText(const nsAString& aText, |
|
986 nsIMobileMessageCallback* aRequest) |
|
987 { |
|
988 #ifndef MOZ_WEBSMS_BACKEND |
|
989 return NS_ERROR_FAILURE; |
|
990 #else |
|
991 ALOG_BRIDGE("AndroidBridge::GetSegmentInfoForText"); |
|
992 |
|
993 dom::mobilemessage::SmsSegmentInfoData data; |
|
994 |
|
995 data.segments() = 0; |
|
996 data.charsPerSegment() = 0; |
|
997 data.charsAvailableInLastSegment() = 0; |
|
998 |
|
999 JNIEnv *env = GetJNIEnv(); |
|
1000 |
|
1001 AutoLocalJNIFrame jniFrame(env, 2); |
|
1002 jstring jText = NewJavaString(&jniFrame, aText); |
|
1003 jobject obj = env->CallStaticObjectMethod(mAndroidSmsMessageClass, |
|
1004 jCalculateLength, jText, JNI_FALSE); |
|
1005 if (jniFrame.CheckForException()) |
|
1006 return NS_ERROR_FAILURE; |
|
1007 |
|
1008 jintArray arr = static_cast<jintArray>(obj); |
|
1009 if (!arr || env->GetArrayLength(arr) != 4) |
|
1010 return NS_ERROR_FAILURE; |
|
1011 |
|
1012 jint* info = env->GetIntArrayElements(arr, JNI_FALSE); |
|
1013 |
|
1014 data.segments() = info[0]; // msgCount |
|
1015 data.charsPerSegment() = info[2]; // codeUnitsRemaining |
|
1016 // segmentChars = (codeUnitCount + codeUnitsRemaining) / msgCount |
|
1017 data.charsAvailableInLastSegment() = (info[1] + info[2]) / info[0]; |
|
1018 |
|
1019 env->ReleaseIntArrayElements(arr, info, JNI_ABORT); |
|
1020 |
|
1021 // TODO Bug 908598 - Should properly use |QueueSmsRequest(...)| to queue up |
|
1022 // the nsIMobileMessageCallback just like other functions. |
|
1023 nsCOMPtr<nsIDOMMozSmsSegmentInfo> info = new SmsSegmentInfo(data); |
|
1024 return aRequest->NotifySegmentInfoForTextGot(info); |
|
1025 #endif |
|
1026 } |
|
1027 |
|
1028 void |
|
1029 AndroidBridge::SendMessage(const nsAString& aNumber, |
|
1030 const nsAString& aMessage, |
|
1031 nsIMobileMessageCallback* aRequest) |
|
1032 { |
|
1033 ALOG_BRIDGE("AndroidBridge::SendMessage"); |
|
1034 |
|
1035 uint32_t requestId; |
|
1036 if (!QueueSmsRequest(aRequest, &requestId)) |
|
1037 return; |
|
1038 |
|
1039 mozilla::widget::android::GeckoAppShell::SendMessageWrapper(aNumber, aMessage, requestId); |
|
1040 } |
|
1041 |
|
1042 void |
|
1043 AndroidBridge::GetMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest) |
|
1044 { |
|
1045 ALOG_BRIDGE("AndroidBridge::GetMessage"); |
|
1046 |
|
1047 uint32_t requestId; |
|
1048 if (!QueueSmsRequest(aRequest, &requestId)) |
|
1049 return; |
|
1050 |
|
1051 mozilla::widget::android::GeckoAppShell::GetMessageWrapper(aMessageId, requestId); |
|
1052 } |
|
1053 |
|
1054 void |
|
1055 AndroidBridge::DeleteMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest) |
|
1056 { |
|
1057 ALOG_BRIDGE("AndroidBridge::DeleteMessage"); |
|
1058 |
|
1059 uint32_t requestId; |
|
1060 if (!QueueSmsRequest(aRequest, &requestId)) |
|
1061 return; |
|
1062 |
|
1063 mozilla::widget::android::GeckoAppShell::DeleteMessageWrapper(aMessageId, requestId); |
|
1064 } |
|
1065 |
|
1066 void |
|
1067 AndroidBridge::CreateMessageList(const dom::mobilemessage::SmsFilterData& aFilter, bool aReverse, |
|
1068 nsIMobileMessageCallback* aRequest) |
|
1069 { |
|
1070 ALOG_BRIDGE("AndroidBridge::CreateMessageList"); |
|
1071 |
|
1072 JNIEnv *env = GetJNIEnv(); |
|
1073 |
|
1074 uint32_t requestId; |
|
1075 if (!QueueSmsRequest(aRequest, &requestId)) |
|
1076 return; |
|
1077 |
|
1078 AutoLocalJNIFrame jniFrame(env, 2); |
|
1079 |
|
1080 jobjectArray numbers = |
|
1081 (jobjectArray)env->NewObjectArray(aFilter.numbers().Length(), |
|
1082 jStringClass, |
|
1083 NewJavaString(&jniFrame, EmptyString())); |
|
1084 |
|
1085 for (uint32_t i = 0; i < aFilter.numbers().Length(); ++i) { |
|
1086 jstring elem = NewJavaString(&jniFrame, aFilter.numbers()[i]); |
|
1087 env->SetObjectArrayElement(numbers, i, elem); |
|
1088 env->DeleteLocalRef(elem); |
|
1089 } |
|
1090 |
|
1091 mozilla::widget::android::GeckoAppShell::CreateMessageListWrapper(aFilter.startDate(), |
|
1092 aFilter.endDate(), numbers, aFilter.numbers().Length(), |
|
1093 aFilter.delivery(), aReverse, requestId); |
|
1094 } |
|
1095 |
|
1096 void |
|
1097 AndroidBridge::GetNextMessageInList(int32_t aListId, nsIMobileMessageCallback* aRequest) |
|
1098 { |
|
1099 ALOG_BRIDGE("AndroidBridge::GetNextMessageInList"); |
|
1100 |
|
1101 uint32_t requestId; |
|
1102 if (!QueueSmsRequest(aRequest, &requestId)) |
|
1103 return; |
|
1104 |
|
1105 mozilla::widget::android::GeckoAppShell::GetNextMessageInListWrapper(aListId, requestId); |
|
1106 } |
|
1107 |
|
1108 bool |
|
1109 AndroidBridge::QueueSmsRequest(nsIMobileMessageCallback* aRequest, uint32_t* aRequestIdOut) |
|
1110 { |
|
1111 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); |
|
1112 MOZ_ASSERT(aRequest && aRequestIdOut); |
|
1113 |
|
1114 const uint32_t length = mSmsRequests.Length(); |
|
1115 for (uint32_t i = 0; i < length; i++) { |
|
1116 if (!(mSmsRequests)[i]) { |
|
1117 (mSmsRequests)[i] = aRequest; |
|
1118 *aRequestIdOut = i; |
|
1119 return true; |
|
1120 } |
|
1121 } |
|
1122 |
|
1123 mSmsRequests.AppendElement(aRequest); |
|
1124 |
|
1125 // After AppendElement(), previous `length` points to the new tail element. |
|
1126 *aRequestIdOut = length; |
|
1127 return true; |
|
1128 } |
|
1129 |
|
1130 already_AddRefed<nsIMobileMessageCallback> |
|
1131 AndroidBridge::DequeueSmsRequest(uint32_t aRequestId) |
|
1132 { |
|
1133 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); |
|
1134 |
|
1135 MOZ_ASSERT(aRequestId < mSmsRequests.Length()); |
|
1136 if (aRequestId >= mSmsRequests.Length()) { |
|
1137 return nullptr; |
|
1138 } |
|
1139 |
|
1140 return mSmsRequests[aRequestId].forget(); |
|
1141 } |
|
1142 |
|
1143 void |
|
1144 AndroidBridge::GetCurrentNetworkInformation(hal::NetworkInformation* aNetworkInfo) |
|
1145 { |
|
1146 ALOG_BRIDGE("AndroidBridge::GetCurrentNetworkInformation"); |
|
1147 |
|
1148 JNIEnv *env = GetJNIEnv(); |
|
1149 |
|
1150 AutoLocalJNIFrame jniFrame(env, 1); |
|
1151 |
|
1152 // To prevent calling too many methods through JNI, the Java method returns |
|
1153 // an array of double even if we actually want an integer, a boolean, and an integer. |
|
1154 |
|
1155 jdoubleArray arr = mozilla::widget::android::GeckoAppShell::GetCurrentNetworkInformationWrapper(); |
|
1156 if (!arr || env->GetArrayLength(arr) != 3) { |
|
1157 return; |
|
1158 } |
|
1159 |
|
1160 jdouble* info = env->GetDoubleArrayElements(arr, 0); |
|
1161 |
|
1162 aNetworkInfo->type() = info[0]; |
|
1163 aNetworkInfo->isWifi() = info[1] == 1.0f; |
|
1164 aNetworkInfo->dhcpGateway() = info[2]; |
|
1165 |
|
1166 env->ReleaseDoubleArrayElements(arr, info, 0); |
|
1167 } |
|
1168 |
|
1169 void * |
|
1170 AndroidBridge::LockBitmap(jobject bitmap) |
|
1171 { |
|
1172 JNIEnv *env = GetJNIEnv(); |
|
1173 |
|
1174 AutoLocalJNIFrame jniFrame(env, 0); |
|
1175 |
|
1176 int err; |
|
1177 void *buf; |
|
1178 |
|
1179 if ((err = AndroidBitmap_lockPixels(env, bitmap, &buf)) != 0) { |
|
1180 ALOG_BRIDGE("AndroidBitmap_lockPixels failed! (error %d)", err); |
|
1181 buf = nullptr; |
|
1182 } |
|
1183 |
|
1184 return buf; |
|
1185 } |
|
1186 |
|
1187 void |
|
1188 AndroidBridge::UnlockBitmap(jobject bitmap) |
|
1189 { |
|
1190 JNIEnv *env = GetJNIEnv(); |
|
1191 |
|
1192 AutoLocalJNIFrame jniFrame(env, 0); |
|
1193 |
|
1194 int err; |
|
1195 |
|
1196 if ((err = AndroidBitmap_unlockPixels(env, bitmap)) != 0) |
|
1197 ALOG_BRIDGE("AndroidBitmap_unlockPixels failed! (error %d)", err); |
|
1198 } |
|
1199 |
|
1200 |
|
1201 bool |
|
1202 AndroidBridge::HasNativeWindowAccess() |
|
1203 { |
|
1204 OpenGraphicsLibraries(); |
|
1205 |
|
1206 // We have a fallback hack in place, so return true if that will work as well |
|
1207 return mHasNativeWindowAccess || mHasNativeWindowFallback; |
|
1208 } |
|
1209 |
|
1210 void* |
|
1211 AndroidBridge::AcquireNativeWindow(JNIEnv* aEnv, jobject aSurface) |
|
1212 { |
|
1213 OpenGraphicsLibraries(); |
|
1214 |
|
1215 if (mHasNativeWindowAccess) |
|
1216 return ANativeWindow_fromSurface(aEnv, aSurface); |
|
1217 |
|
1218 if (mHasNativeWindowFallback) |
|
1219 return GetNativeSurface(aEnv, aSurface); |
|
1220 |
|
1221 return nullptr; |
|
1222 } |
|
1223 |
|
1224 void |
|
1225 AndroidBridge::ReleaseNativeWindow(void *window) |
|
1226 { |
|
1227 if (!window) |
|
1228 return; |
|
1229 |
|
1230 if (mHasNativeWindowAccess) |
|
1231 ANativeWindow_release(window); |
|
1232 |
|
1233 // XXX: we don't ref the pointer we get from the fallback (GetNativeSurface), so we |
|
1234 // have nothing to do here. We should probably ref it. |
|
1235 } |
|
1236 |
|
1237 void* |
|
1238 AndroidBridge::AcquireNativeWindowFromSurfaceTexture(JNIEnv* aEnv, jobject aSurfaceTexture) |
|
1239 { |
|
1240 OpenGraphicsLibraries(); |
|
1241 |
|
1242 if (mHasNativeWindowAccess && ANativeWindow_fromSurfaceTexture) |
|
1243 return ANativeWindow_fromSurfaceTexture(aEnv, aSurfaceTexture); |
|
1244 |
|
1245 if (mHasNativeWindowAccess && android_SurfaceTexture_getNativeWindow) { |
|
1246 android::sp<AndroidRefable> window = android_SurfaceTexture_getNativeWindow(aEnv, aSurfaceTexture); |
|
1247 return window.get(); |
|
1248 } |
|
1249 |
|
1250 return nullptr; |
|
1251 } |
|
1252 |
|
1253 void |
|
1254 AndroidBridge::ReleaseNativeWindowForSurfaceTexture(void *window) |
|
1255 { |
|
1256 if (!window) |
|
1257 return; |
|
1258 |
|
1259 // FIXME: we don't ref the pointer we get, so nothing to do currently. We should ref it. |
|
1260 } |
|
1261 |
|
1262 bool |
|
1263 AndroidBridge::LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride) |
|
1264 { |
|
1265 /* Copied from native_window.h in Android NDK (platform-9) */ |
|
1266 typedef struct ANativeWindow_Buffer { |
|
1267 // The number of pixels that are show horizontally. |
|
1268 int32_t width; |
|
1269 |
|
1270 // The number of pixels that are shown vertically. |
|
1271 int32_t height; |
|
1272 |
|
1273 // The number of *pixels* that a line in the buffer takes in |
|
1274 // memory. This may be >= width. |
|
1275 int32_t stride; |
|
1276 |
|
1277 // The format of the buffer. One of WINDOW_FORMAT_* |
|
1278 int32_t format; |
|
1279 |
|
1280 // The actual bits. |
|
1281 void* bits; |
|
1282 |
|
1283 // Do not touch. |
|
1284 uint32_t reserved[6]; |
|
1285 } ANativeWindow_Buffer; |
|
1286 |
|
1287 // Very similar to the above, but the 'usage' field is included. We use this |
|
1288 // in the fallback case when NDK support is not available |
|
1289 struct SurfaceInfo { |
|
1290 uint32_t w; |
|
1291 uint32_t h; |
|
1292 uint32_t s; |
|
1293 uint32_t usage; |
|
1294 uint32_t format; |
|
1295 unsigned char* bits; |
|
1296 uint32_t reserved[2]; |
|
1297 }; |
|
1298 |
|
1299 int err; |
|
1300 *bits = nullptr; |
|
1301 *width = *height = *format = 0; |
|
1302 |
|
1303 if (mHasNativeWindowAccess) { |
|
1304 ANativeWindow_Buffer buffer; |
|
1305 |
|
1306 if ((err = ANativeWindow_lock(window, (void*)&buffer, nullptr)) != 0) { |
|
1307 ALOG_BRIDGE("ANativeWindow_lock failed! (error %d)", err); |
|
1308 return false; |
|
1309 } |
|
1310 |
|
1311 *bits = (unsigned char*)buffer.bits; |
|
1312 *width = buffer.width; |
|
1313 *height = buffer.height; |
|
1314 *format = buffer.format; |
|
1315 *stride = buffer.stride; |
|
1316 } else if (mHasNativeWindowFallback) { |
|
1317 SurfaceInfo info; |
|
1318 |
|
1319 if ((err = Surface_lock(window, &info, nullptr, true)) != 0) { |
|
1320 ALOG_BRIDGE("Surface_lock failed! (error %d)", err); |
|
1321 return false; |
|
1322 } |
|
1323 |
|
1324 *bits = info.bits; |
|
1325 *width = info.w; |
|
1326 *height = info.h; |
|
1327 *format = info.format; |
|
1328 *stride = info.s; |
|
1329 } else return false; |
|
1330 |
|
1331 return true; |
|
1332 } |
|
1333 |
|
1334 jobject |
|
1335 AndroidBridge::GetGlobalContextRef() { |
|
1336 if (sGlobalContext == nullptr) { |
|
1337 JNIEnv *env = GetJNIForThread(); |
|
1338 |
|
1339 AutoLocalJNIFrame jniFrame(env, 4); |
|
1340 |
|
1341 jobject context = mozilla::widget::android::GeckoAppShell::GetContext(); |
|
1342 if (!context) { |
|
1343 ALOG_BRIDGE("%s: Could not GetContext()", __FUNCTION__); |
|
1344 return 0; |
|
1345 } |
|
1346 jclass contextClass = env->FindClass("android/content/Context"); |
|
1347 if (!contextClass) { |
|
1348 ALOG_BRIDGE("%s: Could not find Context class.", __FUNCTION__); |
|
1349 return 0; |
|
1350 } |
|
1351 jmethodID mid = env->GetMethodID(contextClass, "getApplicationContext", |
|
1352 "()Landroid/content/Context;"); |
|
1353 if (!mid) { |
|
1354 ALOG_BRIDGE("%s: Could not find getApplicationContext.", __FUNCTION__); |
|
1355 return 0; |
|
1356 } |
|
1357 jobject appContext = env->CallObjectMethod(context, mid); |
|
1358 if (!appContext) { |
|
1359 ALOG_BRIDGE("%s: getApplicationContext failed.", __FUNCTION__); |
|
1360 return 0; |
|
1361 } |
|
1362 |
|
1363 sGlobalContext = env->NewGlobalRef(appContext); |
|
1364 MOZ_ASSERT(sGlobalContext); |
|
1365 } |
|
1366 |
|
1367 return sGlobalContext; |
|
1368 } |
|
1369 |
|
1370 bool |
|
1371 AndroidBridge::UnlockWindow(void* window) |
|
1372 { |
|
1373 int err; |
|
1374 |
|
1375 if (!HasNativeWindowAccess()) |
|
1376 return false; |
|
1377 |
|
1378 if (mHasNativeWindowAccess && (err = ANativeWindow_unlockAndPost(window)) != 0) { |
|
1379 ALOG_BRIDGE("ANativeWindow_unlockAndPost failed! (error %d)", err); |
|
1380 return false; |
|
1381 } else if (mHasNativeWindowFallback && (err = Surface_unlockAndPost(window)) != 0) { |
|
1382 ALOG_BRIDGE("Surface_unlockAndPost failed! (error %d)", err); |
|
1383 return false; |
|
1384 } |
|
1385 |
|
1386 return true; |
|
1387 } |
|
1388 |
|
1389 void |
|
1390 AndroidBridge::SetFirstPaintViewport(const LayerIntPoint& aOffset, const CSSToLayerScale& aZoom, const CSSRect& aCssPageRect) |
|
1391 { |
|
1392 mozilla::widget::android::GeckoLayerClient *client = mLayerClient; |
|
1393 if (!client) |
|
1394 return; |
|
1395 |
|
1396 client->SetFirstPaintViewport((float)aOffset.x, (float)aOffset.y, aZoom.scale, |
|
1397 aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost()); |
|
1398 } |
|
1399 |
|
1400 void |
|
1401 AndroidBridge::SetPageRect(const CSSRect& aCssPageRect) |
|
1402 { |
|
1403 mozilla::widget::android::GeckoLayerClient *client = mLayerClient; |
|
1404 if (!client) |
|
1405 return; |
|
1406 |
|
1407 client->SetPageRect(aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost()); |
|
1408 } |
|
1409 |
|
1410 void |
|
1411 AndroidBridge::SyncViewportInfo(const LayerIntRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution, |
|
1412 bool aLayersUpdated, ScreenPoint& aScrollOffset, CSSToScreenScale& aScale, |
|
1413 LayerMargin& aFixedLayerMargins, ScreenPoint& aOffset) |
|
1414 { |
|
1415 mozilla::widget::android::GeckoLayerClient *client = mLayerClient; |
|
1416 if (!client) { |
|
1417 ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); |
|
1418 return; |
|
1419 } |
|
1420 |
|
1421 jobject viewTransformJObj = client->SyncViewportInfo(aDisplayPort.x, aDisplayPort.y, |
|
1422 aDisplayPort.width, aDisplayPort.height, |
|
1423 aDisplayResolution.scale, aLayersUpdated); |
|
1424 NS_ABORT_IF_FALSE(viewTransformJObj, "No view transform object!"); |
|
1425 |
|
1426 if (!viewTransformJObj) { |
|
1427 return; |
|
1428 } |
|
1429 |
|
1430 ViewTransform* viewTransform = ViewTransform::Wrap(viewTransformJObj); |
|
1431 aScrollOffset = ScreenPoint(viewTransform->getx(), viewTransform->gety()); |
|
1432 aScale.scale = viewTransform->getscale(); |
|
1433 aFixedLayerMargins.top = viewTransform->getfixedLayerMarginTop(); |
|
1434 aFixedLayerMargins.right = viewTransform->getfixedLayerMarginRight(); |
|
1435 aFixedLayerMargins.bottom = viewTransform->getfixedLayerMarginBottom(); |
|
1436 aFixedLayerMargins.left = viewTransform->getfixedLayerMarginLeft(); |
|
1437 aOffset.x = viewTransform->getoffsetX(); |
|
1438 aOffset.y = viewTransform->getoffsetY(); |
|
1439 delete viewTransform; |
|
1440 } |
|
1441 |
|
1442 void AndroidBridge::SyncFrameMetrics(const ScreenPoint& aScrollOffset, float aZoom, const CSSRect& aCssPageRect, |
|
1443 bool aLayersUpdated, const CSSRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution, |
|
1444 bool aIsFirstPaint, LayerMargin& aFixedLayerMargins, ScreenPoint& aOffset) |
|
1445 { |
|
1446 mozilla::widget::android::GeckoLayerClient *client = mLayerClient; |
|
1447 if (!client) { |
|
1448 ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); |
|
1449 return; |
|
1450 } |
|
1451 |
|
1452 // convert the displayport rect from scroll-relative CSS pixels to document-relative device pixels |
|
1453 LayerRect dpUnrounded = aDisplayPort * aDisplayResolution; |
|
1454 dpUnrounded += LayerPoint::FromUnknownPoint(aScrollOffset.ToUnknownPoint()); |
|
1455 LayerIntRect dp = gfx::RoundedToInt(dpUnrounded); |
|
1456 |
|
1457 jobject viewTransformJObj = client->SyncFrameMetrics(aScrollOffset.x, aScrollOffset.y, aZoom, |
|
1458 aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost(), |
|
1459 aLayersUpdated, dp.x, dp.y, dp.width, dp.height, aDisplayResolution.scale, |
|
1460 aIsFirstPaint); |
|
1461 |
|
1462 NS_ABORT_IF_FALSE(viewTransformJObj, "No view transform object!"); |
|
1463 if (!viewTransformJObj) { |
|
1464 return; |
|
1465 } |
|
1466 ViewTransform* viewTransform = ViewTransform::Wrap(viewTransformJObj); |
|
1467 |
|
1468 aFixedLayerMargins.top = viewTransform->getfixedLayerMarginTop(); |
|
1469 aFixedLayerMargins.right = viewTransform->getfixedLayerMarginRight(); |
|
1470 aFixedLayerMargins.bottom = viewTransform->getfixedLayerMarginBottom(); |
|
1471 aFixedLayerMargins.left = viewTransform->getfixedLayerMarginLeft(); |
|
1472 |
|
1473 aOffset.x = viewTransform->getoffsetX(); |
|
1474 aOffset.y = viewTransform->getoffsetY(); |
|
1475 |
|
1476 delete viewTransform; |
|
1477 } |
|
1478 |
|
1479 AndroidBridge::AndroidBridge() |
|
1480 : mLayerClient(nullptr), |
|
1481 mNativePanZoomController(nullptr) |
|
1482 { |
|
1483 } |
|
1484 |
|
1485 AndroidBridge::~AndroidBridge() |
|
1486 { |
|
1487 } |
|
1488 |
|
1489 /* Implementation file */ |
|
1490 NS_IMPL_ISUPPORTS(nsAndroidBridge, nsIAndroidBridge) |
|
1491 |
|
1492 nsAndroidBridge::nsAndroidBridge() |
|
1493 { |
|
1494 } |
|
1495 |
|
1496 nsAndroidBridge::~nsAndroidBridge() |
|
1497 { |
|
1498 } |
|
1499 |
|
1500 /* void handleGeckoEvent (in AString message); */ |
|
1501 NS_IMETHODIMP nsAndroidBridge::HandleGeckoMessage(JS::HandleValue val, |
|
1502 JSContext *cx) |
|
1503 { |
|
1504 if (val.isObject()) { |
|
1505 JS::RootedObject object(cx, &val.toObject()); |
|
1506 AndroidBridge::Bridge()->HandleGeckoMessage(cx, object); |
|
1507 return NS_OK; |
|
1508 } |
|
1509 |
|
1510 // Now handle legacy JSON messages. |
|
1511 if (!val.isString()) { |
|
1512 return NS_ERROR_INVALID_ARG; |
|
1513 } |
|
1514 JS::RootedString jsonStr(cx, val.toString()); |
|
1515 |
|
1516 size_t strLen = 0; |
|
1517 const jschar* strChar = JS_GetStringCharsAndLength(cx, jsonStr, &strLen); |
|
1518 if (!strChar) { |
|
1519 return NS_ERROR_UNEXPECTED; |
|
1520 } |
|
1521 |
|
1522 JS::RootedValue jsonVal(cx); |
|
1523 if (!JS_ParseJSON(cx, strChar, strLen, &jsonVal) || !jsonVal.isObject()) { |
|
1524 return NS_ERROR_INVALID_ARG; |
|
1525 } |
|
1526 |
|
1527 // Spit out a warning before sending the message. |
|
1528 nsContentUtils::ReportToConsoleNonLocalized( |
|
1529 NS_LITERAL_STRING("Use of JSON is deprecated. " |
|
1530 "Please pass Javascript objects directly to handleGeckoMessage."), |
|
1531 nsIScriptError::warningFlag, |
|
1532 NS_LITERAL_CSTRING("nsIAndroidBridge"), |
|
1533 nullptr); |
|
1534 |
|
1535 JS::RootedObject object(cx, &jsonVal.toObject()); |
|
1536 AndroidBridge::Bridge()->HandleGeckoMessage(cx, object); |
|
1537 return NS_OK; |
|
1538 } |
|
1539 |
|
1540 /* nsIAndroidDisplayport getDisplayPort(in boolean aPageSizeUpdate, in boolean isBrowserContentDisplayed, in int32_t tabId, in nsIAndroidViewport metrics); */ |
|
1541 NS_IMETHODIMP nsAndroidBridge::GetDisplayPort(bool aPageSizeUpdate, bool aIsBrowserContentDisplayed, int32_t tabId, nsIAndroidViewport* metrics, nsIAndroidDisplayport** displayPort) |
|
1542 { |
|
1543 AndroidBridge::Bridge()->GetDisplayPort(aPageSizeUpdate, aIsBrowserContentDisplayed, tabId, metrics, displayPort); |
|
1544 return NS_OK; |
|
1545 } |
|
1546 |
|
1547 /* void displayedDocumentChanged(); */ |
|
1548 NS_IMETHODIMP nsAndroidBridge::ContentDocumentChanged() |
|
1549 { |
|
1550 AndroidBridge::Bridge()->ContentDocumentChanged(); |
|
1551 return NS_OK; |
|
1552 } |
|
1553 |
|
1554 /* boolean isContentDocumentDisplayed(); */ |
|
1555 NS_IMETHODIMP nsAndroidBridge::IsContentDocumentDisplayed(bool *aRet) |
|
1556 { |
|
1557 *aRet = AndroidBridge::Bridge()->IsContentDocumentDisplayed(); |
|
1558 return NS_OK; |
|
1559 } |
|
1560 |
|
1561 // DO NOT USE THIS unless you need to access JNI from |
|
1562 // non-main threads. This is probably not what you want. |
|
1563 // Questions, ask blassey or dougt. |
|
1564 |
|
1565 static void |
|
1566 JavaThreadDetachFunc(void *arg) |
|
1567 { |
|
1568 JNIEnv *env = (JNIEnv*) arg; |
|
1569 MOZ_ASSERT(env, "No JNIEnv on Gecko thread"); |
|
1570 if (!env) { |
|
1571 return; |
|
1572 } |
|
1573 JavaVM *vm = nullptr; |
|
1574 env->GetJavaVM(&vm); |
|
1575 MOZ_ASSERT(vm, "No JavaVM on Gecko thread"); |
|
1576 if (!vm) { |
|
1577 return; |
|
1578 } |
|
1579 vm->DetachCurrentThread(); |
|
1580 } |
|
1581 |
|
1582 uint32_t |
|
1583 AndroidBridge::GetScreenOrientation() |
|
1584 { |
|
1585 ALOG_BRIDGE("AndroidBridge::GetScreenOrientation"); |
|
1586 |
|
1587 int16_t orientation = mozilla::widget::android::GeckoAppShell::GetScreenOrientationWrapper(); |
|
1588 |
|
1589 if (!orientation) |
|
1590 return dom::eScreenOrientation_None; |
|
1591 |
|
1592 return static_cast<dom::ScreenOrientation>(orientation); |
|
1593 } |
|
1594 |
|
1595 void |
|
1596 AndroidBridge::ScheduleComposite() |
|
1597 { |
|
1598 nsWindow::ScheduleComposite(); |
|
1599 } |
|
1600 |
|
1601 nsresult |
|
1602 AndroidBridge::GetProxyForURI(const nsACString & aSpec, |
|
1603 const nsACString & aScheme, |
|
1604 const nsACString & aHost, |
|
1605 const int32_t aPort, |
|
1606 nsACString & aResult) |
|
1607 { |
|
1608 if (!HasEnv()) { |
|
1609 return NS_ERROR_FAILURE; |
|
1610 } |
|
1611 JNIEnv* env = GetJNIEnv(); |
|
1612 |
|
1613 AutoLocalJNIFrame jniFrame(env, 1); |
|
1614 jstring jstrRet = |
|
1615 mozilla::widget::android::GeckoAppShell::GetProxyForURIWrapper(NS_ConvertUTF8toUTF16(aSpec), |
|
1616 NS_ConvertUTF8toUTF16(aScheme), |
|
1617 NS_ConvertUTF8toUTF16(aHost), |
|
1618 aPort); |
|
1619 |
|
1620 if (!jstrRet) |
|
1621 return NS_ERROR_FAILURE; |
|
1622 |
|
1623 nsJNIString jniStr(jstrRet, env); |
|
1624 CopyUTF16toUTF8(jniStr, aResult); |
|
1625 return NS_OK; |
|
1626 } |
|
1627 |
|
1628 |
|
1629 /* attribute nsIAndroidBrowserApp browserApp; */ |
|
1630 NS_IMETHODIMP nsAndroidBridge::GetBrowserApp(nsIAndroidBrowserApp * *aBrowserApp) |
|
1631 { |
|
1632 if (nsAppShell::gAppShell) |
|
1633 nsAppShell::gAppShell->GetBrowserApp(aBrowserApp); |
|
1634 return NS_OK; |
|
1635 } |
|
1636 |
|
1637 NS_IMETHODIMP nsAndroidBridge::SetBrowserApp(nsIAndroidBrowserApp *aBrowserApp) |
|
1638 { |
|
1639 if (nsAppShell::gAppShell) |
|
1640 nsAppShell::gAppShell->SetBrowserApp(aBrowserApp); |
|
1641 return NS_OK; |
|
1642 } |
|
1643 |
|
1644 void |
|
1645 AndroidBridge::AddPluginView(jobject view, const LayoutDeviceRect& rect, bool isFullScreen) { |
|
1646 nsWindow* win = nsWindow::TopWindow(); |
|
1647 if (!win) |
|
1648 return; |
|
1649 |
|
1650 CSSRect cssRect = rect / win->GetDefaultScale(); |
|
1651 mozilla::widget::android::GeckoAppShell::AddPluginViewWrapper(view, cssRect.x, cssRect.y, |
|
1652 cssRect.width, cssRect.height, isFullScreen); |
|
1653 } |
|
1654 |
|
1655 extern "C" |
|
1656 __attribute__ ((visibility("default"))) |
|
1657 jobject JNICALL |
|
1658 Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(JNIEnv *env, jclass, jlong size); |
|
1659 |
|
1660 bool |
|
1661 AndroidBridge::GetThreadNameJavaProfiling(uint32_t aThreadId, nsCString & aResult) |
|
1662 { |
|
1663 JNIEnv* env = GetJNIForThread(); |
|
1664 |
|
1665 AutoLocalJNIFrame jniFrame(env, 1); |
|
1666 |
|
1667 jstring jstrThreadName = |
|
1668 mozilla::widget::android::GeckoJavaSampler::GetThreadNameJavaProfilingWrapper(aThreadId); |
|
1669 |
|
1670 if (!jstrThreadName) |
|
1671 return false; |
|
1672 |
|
1673 nsJNIString jniStr(jstrThreadName, env); |
|
1674 CopyUTF16toUTF8(jniStr.get(), aResult); |
|
1675 return true; |
|
1676 } |
|
1677 |
|
1678 bool |
|
1679 AndroidBridge::GetFrameNameJavaProfiling(uint32_t aThreadId, uint32_t aSampleId, |
|
1680 uint32_t aFrameId, nsCString & aResult) |
|
1681 { |
|
1682 JNIEnv* env = GetJNIForThread(); |
|
1683 |
|
1684 AutoLocalJNIFrame jniFrame(env, 1); |
|
1685 |
|
1686 jstring jstrSampleName = mozilla::widget::android::GeckoJavaSampler::GetFrameNameJavaProfilingWrapper |
|
1687 (aThreadId, aSampleId, aFrameId); |
|
1688 |
|
1689 if (!jstrSampleName) |
|
1690 return false; |
|
1691 |
|
1692 nsJNIString jniStr(jstrSampleName, env); |
|
1693 CopyUTF16toUTF8(jniStr.get(), aResult); |
|
1694 return true; |
|
1695 } |
|
1696 |
|
1697 nsresult AndroidBridge::CaptureThumbnail(nsIDOMWindow *window, int32_t bufW, int32_t bufH, int32_t tabId, jobject buffer) |
|
1698 { |
|
1699 nsresult rv; |
|
1700 float scale = 1.0; |
|
1701 |
|
1702 if (!buffer) |
|
1703 return NS_ERROR_FAILURE; |
|
1704 |
|
1705 // take a screenshot, as wide as possible, proportional to the destination size |
|
1706 nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window); |
|
1707 if (!utils) |
|
1708 return NS_ERROR_FAILURE; |
|
1709 |
|
1710 nsCOMPtr<nsIDOMClientRect> rect; |
|
1711 rv = utils->GetRootBounds(getter_AddRefs(rect)); |
|
1712 NS_ENSURE_SUCCESS(rv, rv); |
|
1713 if (!rect) |
|
1714 return NS_ERROR_FAILURE; |
|
1715 |
|
1716 float left, top, width, height; |
|
1717 rect->GetLeft(&left); |
|
1718 rect->GetTop(&top); |
|
1719 rect->GetWidth(&width); |
|
1720 rect->GetHeight(&height); |
|
1721 |
|
1722 if (width == 0 || height == 0) |
|
1723 return NS_ERROR_FAILURE; |
|
1724 |
|
1725 int32_t srcX = left; |
|
1726 int32_t srcY = top; |
|
1727 int32_t srcW; |
|
1728 int32_t srcH; |
|
1729 |
|
1730 float aspectRatio = ((float) bufW) / bufH; |
|
1731 if (width / aspectRatio < height) { |
|
1732 srcW = width; |
|
1733 srcH = width / aspectRatio; |
|
1734 } else { |
|
1735 srcW = height * aspectRatio; |
|
1736 srcH = height; |
|
1737 } |
|
1738 |
|
1739 JNIEnv* env = GetJNIEnv(); |
|
1740 |
|
1741 AutoLocalJNIFrame jniFrame(env, 0); |
|
1742 |
|
1743 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(window); |
|
1744 if (!win) |
|
1745 return NS_ERROR_FAILURE; |
|
1746 nsRefPtr<nsPresContext> presContext; |
|
1747 nsIDocShell* docshell = win->GetDocShell(); |
|
1748 if (docshell) { |
|
1749 docshell->GetPresContext(getter_AddRefs(presContext)); |
|
1750 } |
|
1751 if (!presContext) |
|
1752 return NS_ERROR_FAILURE; |
|
1753 nscolor bgColor = NS_RGB(255, 255, 255); |
|
1754 nsCOMPtr<nsIPresShell> presShell = presContext->PresShell(); |
|
1755 uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING | |
|
1756 nsIPresShell::RENDER_DOCUMENT_RELATIVE); |
|
1757 nsRect r(nsPresContext::CSSPixelsToAppUnits(srcX / scale), |
|
1758 nsPresContext::CSSPixelsToAppUnits(srcY / scale), |
|
1759 nsPresContext::CSSPixelsToAppUnits(srcW / scale), |
|
1760 nsPresContext::CSSPixelsToAppUnits(srcH / scale)); |
|
1761 |
|
1762 bool is24bit = (GetScreenDepth() == 24); |
|
1763 uint32_t stride = bufW * (is24bit ? 4 : 2); |
|
1764 |
|
1765 void* data = env->GetDirectBufferAddress(buffer); |
|
1766 if (!data) |
|
1767 return NS_ERROR_FAILURE; |
|
1768 |
|
1769 nsRefPtr<gfxImageSurface> surf = |
|
1770 new gfxImageSurface(static_cast<unsigned char*>(data), nsIntSize(bufW, bufH), stride, |
|
1771 is24bit ? gfxImageFormat::RGB24 : |
|
1772 gfxImageFormat::RGB16_565); |
|
1773 if (surf->CairoStatus() != 0) { |
|
1774 ALOG_BRIDGE("Error creating gfxImageSurface"); |
|
1775 return NS_ERROR_FAILURE; |
|
1776 } |
|
1777 |
|
1778 nsRefPtr<gfxContext> context; |
|
1779 if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(BackendType::CAIRO)) { |
|
1780 RefPtr<DrawTarget> dt = |
|
1781 gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, IntSize(bufW, bufH)); |
|
1782 |
|
1783 if (!dt) { |
|
1784 ALOG_BRIDGE("Error creating DrawTarget"); |
|
1785 return NS_ERROR_FAILURE; |
|
1786 } |
|
1787 context = new gfxContext(dt); |
|
1788 } else { |
|
1789 context = new gfxContext(surf); |
|
1790 } |
|
1791 gfxPoint pt(0, 0); |
|
1792 context->Translate(pt); |
|
1793 context->Scale(scale * bufW / srcW, scale * bufH / srcH); |
|
1794 rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context); |
|
1795 if (is24bit) { |
|
1796 gfxUtils::ConvertBGRAtoRGBA(surf); |
|
1797 } |
|
1798 NS_ENSURE_SUCCESS(rv, rv); |
|
1799 return NS_OK; |
|
1800 } |
|
1801 |
|
1802 void |
|
1803 AndroidBridge::GetDisplayPort(bool aPageSizeUpdate, bool aIsBrowserContentDisplayed, int32_t tabId, nsIAndroidViewport* metrics, nsIAndroidDisplayport** displayPort) |
|
1804 { |
|
1805 |
|
1806 ALOG_BRIDGE("Enter: %s", __PRETTY_FUNCTION__); |
|
1807 JNIEnv* env = GetJNIEnv(); |
|
1808 if (!mLayerClient || mLayerClient->isNull()) { |
|
1809 |
|
1810 ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); |
|
1811 return; |
|
1812 } |
|
1813 AutoLocalJNIFrame jniFrame(env, 0); |
|
1814 |
|
1815 float x, y, width, height, |
|
1816 pageLeft, pageTop, pageRight, pageBottom, |
|
1817 cssPageLeft, cssPageTop, cssPageRight, cssPageBottom, |
|
1818 zoom; |
|
1819 metrics->GetX(&x); |
|
1820 metrics->GetY(&y); |
|
1821 metrics->GetWidth(&width); |
|
1822 metrics->GetHeight(&height); |
|
1823 metrics->GetPageLeft(&pageLeft); |
|
1824 metrics->GetPageTop(&pageTop); |
|
1825 metrics->GetPageRight(&pageRight); |
|
1826 metrics->GetPageBottom(&pageBottom); |
|
1827 metrics->GetCssPageLeft(&cssPageLeft); |
|
1828 metrics->GetCssPageTop(&cssPageTop); |
|
1829 metrics->GetCssPageRight(&cssPageRight); |
|
1830 metrics->GetCssPageBottom(&cssPageBottom); |
|
1831 metrics->GetZoom(&zoom); |
|
1832 |
|
1833 ImmutableViewportMetrics jmetrics = ImmutableViewportMetrics(pageLeft, pageTop, pageRight, pageBottom, |
|
1834 cssPageLeft, cssPageTop, cssPageRight, cssPageBottom, |
|
1835 x, y, x + width, y + height, |
|
1836 zoom); |
|
1837 |
|
1838 jobject jobj = mLayerClient->GetDisplayPort(aPageSizeUpdate, aIsBrowserContentDisplayed, tabId, jmetrics.wrappedObject()); |
|
1839 if (!jobj) { |
|
1840 ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); |
|
1841 return; |
|
1842 } |
|
1843 DisplayPortMetrics* displayPortMetrics = DisplayPortMetrics::Wrap(jobj); |
|
1844 |
|
1845 AndroidRectF rect(env, displayPortMetrics->getMPosition()); |
|
1846 if (jniFrame.CheckForException()) { |
|
1847 ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); |
|
1848 return; |
|
1849 } |
|
1850 |
|
1851 float resolution = displayPortMetrics->getResolution(); |
|
1852 if (jniFrame.CheckForException()) { |
|
1853 ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); |
|
1854 return; |
|
1855 } |
|
1856 |
|
1857 *displayPort = new nsAndroidDisplayport(rect, resolution); |
|
1858 (*displayPort)->AddRef(); |
|
1859 |
|
1860 delete displayPortMetrics; |
|
1861 ALOG_BRIDGE("Exit: %s", __PRETTY_FUNCTION__); |
|
1862 } |
|
1863 |
|
1864 void |
|
1865 AndroidBridge::ContentDocumentChanged() |
|
1866 { |
|
1867 if (!mLayerClient) { |
|
1868 return; |
|
1869 } |
|
1870 mLayerClient->ContentDocumentChanged(); |
|
1871 } |
|
1872 |
|
1873 bool |
|
1874 AndroidBridge::IsContentDocumentDisplayed() |
|
1875 { |
|
1876 if (!mLayerClient) |
|
1877 return false; |
|
1878 |
|
1879 return mLayerClient->IsContentDocumentDisplayed(); |
|
1880 } |
|
1881 |
|
1882 bool |
|
1883 AndroidBridge::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, const LayerRect& aDisplayPort, float aDisplayResolution, bool aDrawingCritical, ParentLayerRect& aCompositionBounds, CSSToParentLayerScale& aZoom) |
|
1884 { |
|
1885 mozilla::widget::android::GeckoLayerClient *client = mLayerClient; |
|
1886 if (!client) { |
|
1887 ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); |
|
1888 return false; |
|
1889 } |
|
1890 |
|
1891 jobject progressiveUpdateDataJObj = client->ProgressiveUpdateCallback(aHasPendingNewThebesContent, |
|
1892 (float)aDisplayPort.x, |
|
1893 (float)aDisplayPort.y, |
|
1894 (float)aDisplayPort.width, |
|
1895 (float)aDisplayPort.height, |
|
1896 aDisplayResolution, |
|
1897 !aDrawingCritical); |
|
1898 |
|
1899 NS_ABORT_IF_FALSE(progressiveUpdateDataJObj, "No progressive update data!"); |
|
1900 |
|
1901 ProgressiveUpdateData* progressiveUpdateData = ProgressiveUpdateData::Wrap(progressiveUpdateDataJObj); |
|
1902 |
|
1903 aCompositionBounds.x = progressiveUpdateData->getx(); |
|
1904 aCompositionBounds.y = progressiveUpdateData->gety(); |
|
1905 aCompositionBounds.width = progressiveUpdateData->getwidth(); |
|
1906 aCompositionBounds.height = progressiveUpdateData->getheight(); |
|
1907 aZoom.scale = progressiveUpdateData->getscale(); |
|
1908 |
|
1909 bool ret = progressiveUpdateData->getabort(); |
|
1910 delete progressiveUpdateData; |
|
1911 |
|
1912 return ret; |
|
1913 } |
|
1914 |
|
1915 mozilla::widget::android::NativePanZoomController* |
|
1916 AndroidBridge::SetNativePanZoomController(jobject obj) |
|
1917 { |
|
1918 mozilla::widget::android::NativePanZoomController* old = mNativePanZoomController; |
|
1919 mNativePanZoomController = mozilla::widget::android::NativePanZoomController::Wrap(obj); |
|
1920 return old; |
|
1921 } |
|
1922 |
|
1923 void |
|
1924 AndroidBridge::RequestContentRepaint(const mozilla::layers::FrameMetrics& aFrameMetrics) |
|
1925 { |
|
1926 ALOG_BRIDGE("AndroidBridge::RequestContentRepaint"); |
|
1927 |
|
1928 // FIXME implement this |
|
1929 } |
|
1930 |
|
1931 void |
|
1932 AndroidBridge::AcknowledgeScrollUpdate(const mozilla::layers::FrameMetrics::ViewID& aScrollId, |
|
1933 const uint32_t& aScrollGeneration) |
|
1934 { |
|
1935 // FIXME implement this |
|
1936 } |
|
1937 |
|
1938 void |
|
1939 AndroidBridge::HandleDoubleTap(const CSSPoint& aPoint, |
|
1940 int32_t aModifiers, |
|
1941 const mozilla::layers::ScrollableLayerGuid& aGuid) |
|
1942 { |
|
1943 nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y); |
|
1944 nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent( |
|
1945 NS_LITERAL_CSTRING("Gesture:DoubleTap"), data)); |
|
1946 } |
|
1947 |
|
1948 void |
|
1949 AndroidBridge::HandleSingleTap(const CSSPoint& aPoint, |
|
1950 int32_t aModifiers, |
|
1951 const mozilla::layers::ScrollableLayerGuid& aGuid) |
|
1952 { |
|
1953 // TODO Send the modifier data to Gecko for use in mouse events. |
|
1954 nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y); |
|
1955 nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent( |
|
1956 NS_LITERAL_CSTRING("Gesture:SingleTap"), data)); |
|
1957 } |
|
1958 |
|
1959 void |
|
1960 AndroidBridge::HandleLongTap(const CSSPoint& aPoint, |
|
1961 int32_t aModifiers, |
|
1962 const mozilla::layers::ScrollableLayerGuid& aGuid) |
|
1963 { |
|
1964 nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y); |
|
1965 nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent( |
|
1966 NS_LITERAL_CSTRING("Gesture:LongPress"), data)); |
|
1967 } |
|
1968 |
|
1969 void |
|
1970 AndroidBridge::HandleLongTapUp(const CSSPoint& aPoint, |
|
1971 int32_t aModifiers, |
|
1972 const mozilla::layers::ScrollableLayerGuid& aGuid) |
|
1973 { |
|
1974 } |
|
1975 |
|
1976 void |
|
1977 AndroidBridge::SendAsyncScrollDOMEvent(bool aIsRoot, |
|
1978 const CSSRect& aContentRect, |
|
1979 const CSSSize& aScrollableSize) |
|
1980 { |
|
1981 // FIXME implement this |
|
1982 } |
|
1983 |
|
1984 void |
|
1985 AndroidBridge::PostDelayedTask(Task* aTask, int aDelayMs) |
|
1986 { |
|
1987 // add the new task into the mDelayedTaskQueue, sorted with |
|
1988 // the earliest task first in the queue |
|
1989 DelayedTask* newTask = new DelayedTask(aTask, aDelayMs); |
|
1990 uint32_t i = 0; |
|
1991 while (i < mDelayedTaskQueue.Length()) { |
|
1992 if (newTask->IsEarlierThan(mDelayedTaskQueue[i])) { |
|
1993 mDelayedTaskQueue.InsertElementAt(i, newTask); |
|
1994 break; |
|
1995 } |
|
1996 i++; |
|
1997 } |
|
1998 if (i == mDelayedTaskQueue.Length()) { |
|
1999 // this new task will run after all the existing tasks in the queue |
|
2000 mDelayedTaskQueue.AppendElement(newTask); |
|
2001 } |
|
2002 if (i == 0) { |
|
2003 // if we're inserting it at the head of the queue, notify Java because |
|
2004 // we need to get a callback at an earlier time than the last scheduled |
|
2005 // callback |
|
2006 mNativePanZoomController->PostDelayedCallbackWrapper((int64_t)aDelayMs); |
|
2007 } |
|
2008 } |
|
2009 |
|
2010 int64_t |
|
2011 AndroidBridge::RunDelayedTasks() |
|
2012 { |
|
2013 while (mDelayedTaskQueue.Length() > 0) { |
|
2014 DelayedTask* nextTask = mDelayedTaskQueue[0]; |
|
2015 int64_t timeLeft = nextTask->MillisecondsToRunTime(); |
|
2016 if (timeLeft > 0) { |
|
2017 // this task (and therefore all remaining tasks) |
|
2018 // have not yet reached their runtime. return the |
|
2019 // time left until we should be called again |
|
2020 return timeLeft; |
|
2021 } |
|
2022 |
|
2023 // we have a delayed task to run. extract it from |
|
2024 // the wrapper and free the wrapper |
|
2025 |
|
2026 mDelayedTaskQueue.RemoveElementAt(0); |
|
2027 Task* task = nextTask->GetTask(); |
|
2028 delete nextTask; |
|
2029 |
|
2030 task->Run(); |
|
2031 } |
|
2032 return -1; |
|
2033 } |