1 /* -*- Mode: c++; c-basic-offset: 2; tab-width: 4; indent-tabs-mode: nil; -*-
2 * vim: set sw=2 ts=4 expandtab:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include <android/log.h>
8 #include <android/native_window.h>
9 #include <android/native_window_jni.h>
10 #include <math.h>
11 #include <queue>
12 #include <type_traits>
13 #include <unistd.h>
14
15 #include "mozilla/MiscEvents.h"
16 #include "mozilla/MouseEvents.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/RWLock.h"
19 #include "mozilla/StaticPrefs_android.h"
20 #include "mozilla/StaticPrefs_ui.h"
21 #include "mozilla/TouchEvents.h"
22 #include "mozilla/Unused.h"
23 #include "mozilla/WeakPtr.h"
24 #include "mozilla/WheelHandlingHelper.h" // for WheelDeltaAdjustmentStrategy
25
26 #include "mozilla/Preferences.h"
27 #include "mozilla/Unused.h"
28 #include "mozilla/a11y/SessionAccessibility.h"
29 #include "mozilla/dom/ContentChild.h"
30 #include "mozilla/dom/ContentParent.h"
31 #include "mozilla/dom/MouseEventBinding.h"
32 #include "mozilla/gfx/2D.h"
33 #include "mozilla/gfx/DataSurfaceHelpers.h"
34 #include "mozilla/layers/RenderTrace.h"
35 #include <algorithm>
36
37 using mozilla::Unused;
38 using mozilla::dom::ContentChild;
39 using mozilla::dom::ContentParent;
40
41 #include "nsWindow.h"
42
43 #include "AndroidGraphics.h"
44 #include "JavaExceptions.h"
45
46 #include "nsIWidgetListener.h"
47 #include "nsIWindowWatcher.h"
48 #include "nsIAppWindow.h"
49
50 #include "nsAppShell.h"
51 #include "nsFocusManager.h"
52 #include "nsIdleService.h"
53 #include "nsLayoutUtils.h"
54 #include "nsViewManager.h"
55
56 #include "WidgetUtils.h"
57 #include "nsContentUtils.h"
58
59 #include "nsGfxCIID.h"
60 #include "nsGkAtoms.h"
61 #include "nsWidgetsCID.h"
62
63 #include "gfxContext.h"
64
65 #include "AndroidContentController.h"
66 #include "GLContext.h"
67 #include "GLContextProvider.h"
68 #include "Layers.h"
69 #include "ScopedGLHelpers.h"
70 #include "mozilla/layers/APZEventState.h"
71 #include "mozilla/layers/APZInputBridge.h"
72 #include "mozilla/layers/APZThreadUtils.h"
73 #include "mozilla/layers/AsyncCompositionManager.h"
74 #include "mozilla/layers/CompositorOGL.h"
75 #include "mozilla/layers/IAPZCTreeManager.h"
76 #include "mozilla/layers/LayerManagerComposite.h"
77
78 #include "nsTArray.h"
79
80 #include "AndroidBridge.h"
81 #include "AndroidBridgeUtilities.h"
82 #include "AndroidUiThread.h"
83 #include "GeckoEditableSupport.h"
84 #include "KeyEvent.h"
85 #include "MotionEvent.h"
86 #include "mozilla/java/EventDispatcherWrappers.h"
87 #include "mozilla/java/GeckoAppShellWrappers.h"
88 #include "mozilla/java/GeckoEditableChildWrappers.h"
89 #include "mozilla/java/GeckoResultWrappers.h"
90 #include "mozilla/java/GeckoSessionNatives.h"
91 #include "mozilla/java/GeckoSystemStateListenerWrappers.h"
92 #include "mozilla/java/PanZoomControllerNatives.h"
93 #include "mozilla/java/SessionAccessibilityWrappers.h"
94 #include "ScreenHelperAndroid.h"
95
96 #include "GeckoProfiler.h" // For AUTO_PROFILER_LABEL
97 #include "nsPrintfCString.h"
98 #include "nsString.h"
99
100 #include "JavaBuiltins.h"
101
102 #include "mozilla/ipc/Shmem.h"
103
104 using namespace mozilla;
105 using namespace mozilla::dom;
106 using namespace mozilla::layers;
107 using namespace mozilla::widget;
108 using namespace mozilla::ipc;
109
110 using mozilla::java::GeckoSession;
111
112 #include "mozilla/layers/CompositorBridgeChild.h"
113 #include "mozilla/layers/CompositorSession.h"
114 #include "mozilla/layers/LayerTransactionParent.h"
115 #include "mozilla/layers/UiCompositorControllerChild.h"
116 #include "nsThreadUtils.h"
117
118 // All the toplevel windows that have been created; these are in
119 // stacking order, so the window at gTopLevelWindows[0] is the topmost
120 // one.
121 static nsTArray<nsWindow*> gTopLevelWindows;
122
123 static bool sFailedToCreateGLContext = false;
124
125 // Multitouch swipe thresholds in inches
126 static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4;
127 static const double SWIPE_MIN_DISTANCE_INCHES = 0.6;
128
129 static const int32_t INPUT_RESULT_UNHANDLED =
130 java::PanZoomController::INPUT_RESULT_UNHANDLED;
131 static const int32_t INPUT_RESULT_HANDLED =
132 java::PanZoomController::INPUT_RESULT_HANDLED;
133 static const int32_t INPUT_RESULT_HANDLED_CONTENT =
134 java::PanZoomController::INPUT_RESULT_HANDLED_CONTENT;
135
136 template <typename Lambda, bool IsStatic, typename InstanceType, class Impl>
137 class nsWindow::WindowEvent : public Runnable {
IsStaleCall()138 bool IsStaleCall() {
139 if (IsStatic) {
140 // Static calls are never stale.
141 return false;
142 }
143
144 JNIEnv* const env = mozilla::jni::GetEnvForThread();
145
146 const auto natives = reinterpret_cast<mozilla::WeakPtr<Impl>*>(
147 jni::GetNativeHandle(env, mInstance.Get()));
148 MOZ_CATCH_JNI_EXCEPTION(env);
149
150 // The call is stale if the nsWindow has been destroyed on the
151 // Gecko side, but the Java object is still attached to it through
152 // a weak pointer. Stale calls should be discarded. Note that it's
153 // an error if natives is nullptr here; we return false but the
154 // native call will throw an error.
155 return natives && !natives->get();
156 }
157
158 Lambda mLambda;
159 const InstanceType mInstance;
160
161 public:
WindowEvent(Lambda && aLambda,InstanceType && aInstance)162 WindowEvent(Lambda&& aLambda, InstanceType&& aInstance)
163 : Runnable("nsWindowEvent"),
164 mLambda(std::move(aLambda)),
165 mInstance(std::forward<InstanceType>(aInstance)) {}
166
WindowEvent(Lambda && aLambda)167 explicit WindowEvent(Lambda&& aLambda)
168 : Runnable("nsWindowEvent"),
169 mLambda(std::move(aLambda)),
170 mInstance(mLambda.GetThisArg()) {}
171
Run()172 NS_IMETHOD Run() override {
173 if (!IsStaleCall()) {
174 mLambda();
175 }
176 return NS_OK;
177 }
178 };
179
180 namespace {
181 template <class Instance, class Impl>
182 std::enable_if_t<
183 jni::detail::NativePtrPicker<Impl>::value == jni::detail::REFPTR, void>
CallAttachNative(Instance aInstance,Impl * aImpl)184 CallAttachNative(Instance aInstance, Impl* aImpl) {
185 Impl::AttachNative(aInstance, RefPtr<Impl>(aImpl).get());
186 }
187
188 template <class Instance, class Impl>
189 std::enable_if_t<
190 jni::detail::NativePtrPicker<Impl>::value == jni::detail::OWNING, void>
CallAttachNative(Instance aInstance,Impl * aImpl)191 CallAttachNative(Instance aInstance, Impl* aImpl) {
192 Impl::AttachNative(aInstance, UniquePtr<Impl>(aImpl));
193 }
194
195 template <class Lambda>
DispatchToUiThread(const char * aName,Lambda && aLambda)196 bool DispatchToUiThread(const char* aName, Lambda&& aLambda) {
197 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
198 uiThread->Dispatch(NS_NewRunnableFunction(aName, std::move(aLambda)));
199 return true;
200 }
201 return false;
202 }
203 } // namespace
204
205 template <class Impl>
206 template <class Cls, typename... Args>
Attach(const jni::LocalRef<Cls> & aInstance,nsWindow * aWindow,Args &&...aArgs)207 void nsWindow::NativePtr<Impl>::Attach(const jni::LocalRef<Cls>& aInstance,
208 nsWindow* aWindow, Args&&... aArgs) {
209 MOZ_ASSERT(NS_IsMainThread());
210 MOZ_ASSERT(!mPtr && !mImpl);
211
212 Impl* const impl = new Impl(this, aWindow, std::forward<Args>(aArgs)...);
213 mImpl = impl;
214
215 // CallAttachNative transfers ownership of impl.
216 CallAttachNative<>(aInstance, impl);
217 }
218
219 template <class Impl>
220 template <class Cls, typename T>
Detach(const jni::Ref<Cls,T> & aInstance)221 void nsWindow::NativePtr<Impl>::Detach(const jni::Ref<Cls, T>& aInstance) {
222 MOZ_ASSERT(NS_IsMainThread());
223 MOZ_ASSERT(mPtr && mImpl);
224
225 // nsIRunnable that takes care of disposing the native object attached to
226 // the Java object in a safe manner.
227 class ImplDisposer : public Runnable {
228 const typename Cls::GlobalRef mInstance;
229 const uintptr_t mOldImpl;
230
231 public:
232 explicit ImplDisposer(const typename Cls::LocalRef& aInstance)
233 : Runnable("nsWindow::NativePtr::Detach"),
234 mInstance(aInstance.Env(), aInstance),
235 mOldImpl(aInstance
236 ? jni::GetNativeHandle(aInstance.Env(), aInstance.Get())
237 : 0) {
238 MOZ_CATCH_JNI_EXCEPTION(aInstance.Env());
239 }
240
241 NS_IMETHOD Run() override {
242 if (!mInstance) {
243 return NS_OK;
244 }
245
246 if (!NS_IsMainThread()) {
247 NS_DispatchToMainThread(this);
248 return NS_OK;
249 }
250
251 typename Cls::LocalRef instance(jni::GetGeckoThreadEnv(), mInstance);
252 auto newImpl = jni::GetNativeHandle(instance.Env(), instance.Get());
253 MOZ_CATCH_JNI_EXCEPTION(instance.Env());
254
255 if (mOldImpl == newImpl) {
256 // Only dispose the object if the native object has not changed.
257 Impl::DisposeNative(instance);
258 }
259 return NS_OK;
260 }
261 };
262
263 // Objects that use nsWindow::NativePtr are expected to implement a public
264 // member function with signature "void OnDetach(
265 // already_AddRefed<Runnable> aDisposer)". This function should perform
266 // necessary cleanups for the native/Java objects, as well as mark the Java
267 // object as being disposed, so no native methods are called after that
268 // point. After this disposal step, the function must call "aDisposer->
269 // Run()" to finish disposing the native object. The disposer is
270 // thread-safe and may be called on any thread as necessary.
271 mImpl->OnDetach(
272 do_AddRef(new ImplDisposer({jni::GetGeckoThreadEnv(), aInstance})));
273
274 {
275 Locked implLock(*this);
276 mImpl = nullptr;
277 }
278
279 typename WindowPtr<Impl>::Locked lock(*mPtr);
280 mPtr->mWindow = nullptr;
281 mPtr->mPtr = nullptr;
282 mPtr = nullptr;
283 }
284
285 template <class Impl>
286 class nsWindow::NativePtr<Impl>::Locked final : private MutexAutoLock {
287 Impl* const mImpl;
288
289 public:
Locked(NativePtr<Impl> & aPtr)290 explicit Locked(NativePtr<Impl>& aPtr)
291 : MutexAutoLock(aPtr.mImplLock), mImpl(aPtr.mImpl) {}
292
operator Impl*() const293 operator Impl*() const { return mImpl; }
operator ->() const294 Impl* operator->() const { return mImpl; }
295 };
296
297 class nsWindow::GeckoViewSupport final
298 : public GeckoSession::Window::Natives<GeckoViewSupport>,
299 public SupportsWeakPtr<GeckoViewSupport> {
300 nsWindow& window;
301
302 // We hold a WeakRef because we want to allow the
303 // GeckoSession.Window to be garbage collected.
304 // Callers need to create a LocalRef from this
305 // before calling methods.
306 GeckoSession::Window::WeakRef mGeckoViewWindow;
307
308 public:
309 typedef GeckoSession::Window::Natives<GeckoViewSupport> Base;
310 typedef SupportsWeakPtr<GeckoViewSupport> SupportsWeakPtr;
311
312 MOZ_DECLARE_WEAKREFERENCE_TYPENAME(GeckoViewSupport);
313
314 template <typename Functor>
OnNativeCall(Functor && aCall)315 static void OnNativeCall(Functor&& aCall) {
316 NS_DispatchToMainThread(new WindowEvent<Functor>(std::move(aCall)));
317 }
318
GeckoViewSupport(nsWindow * aWindow,const GeckoSession::Window::LocalRef & aInstance)319 GeckoViewSupport(nsWindow* aWindow,
320 const GeckoSession::Window::LocalRef& aInstance)
321 : window(*aWindow), mGeckoViewWindow(aInstance) {
322 Base::AttachNative(aInstance, static_cast<SupportsWeakPtr*>(this));
323 }
324
325 ~GeckoViewSupport();
326
327 using Base::DisposeNative;
328
329 /**
330 * GeckoView methods
331 */
332 private:
333 nsCOMPtr<nsPIDOMWindowOuter> mDOMWindow;
334 bool mIsReady{false};
335
336 public:
337 // Create and attach a window.
338 static void Open(const jni::Class::LocalRef& aCls,
339 GeckoSession::Window::Param aWindow,
340 jni::Object::Param aQueue, jni::Object::Param aCompositor,
341 jni::Object::Param aDispatcher,
342 jni::Object::Param aSessionAccessibility,
343 jni::Object::Param aInitData, jni::String::Param aId,
344 jni::String::Param aChromeURI, int32_t aScreenId,
345 bool aPrivateMode, bool aRemote);
346
347 // Close and destroy the nsWindow.
348 void Close();
349
350 // Transfer this nsWindow to new GeckoSession objects.
351 void Transfer(const GeckoSession::Window::LocalRef& inst,
352 jni::Object::Param aQueue, jni::Object::Param aCompositor,
353 jni::Object::Param aDispatcher,
354 jni::Object::Param aSessionAccessibility,
355 jni::Object::Param aInitData);
356
357 void AttachEditable(const GeckoSession::Window::LocalRef& inst,
358 jni::Object::Param aEditableParent);
359
360 void AttachAccessibility(const GeckoSession::Window::LocalRef& inst,
361 jni::Object::Param aSessionAccessibility);
362
363 void OnReady(jni::Object::Param aQueue = nullptr);
364
365 auto OnLoadRequest(mozilla::jni::String::Param aUri, int32_t aWindowType,
366 int32_t aFlags, mozilla::jni::String::Param aTriggeringUri,
367 bool aHasUserGesture, bool aIsTopLevel) const
368 -> java::GeckoResult::LocalRef;
369 };
370
371 /**
372 * PanZoomController handles its native calls on the UI thread, so make
373 * it separate from GeckoViewSupport.
374 */
375 class nsWindow::NPZCSupport final
376 : public java::PanZoomController::NativeProvider::Natives<NPZCSupport> {
377 using LockedWindowPtr = WindowPtr<NPZCSupport>::Locked;
378
379 WindowPtr<NPZCSupport> mWindow;
380 java::PanZoomController::NativeProvider::WeakRef mNPZC;
381 int mPreviousButtons;
382
383 template <typename Lambda>
384 class InputEvent final : public nsAppShell::Event {
385 java::PanZoomController::NativeProvider::GlobalRef mNPZC;
386 Lambda mLambda;
387
388 public:
InputEvent(const NPZCSupport * aNPZCSupport,Lambda && aLambda)389 InputEvent(const NPZCSupport* aNPZCSupport, Lambda&& aLambda)
390 : mNPZC(aNPZCSupport->mNPZC), mLambda(std::move(aLambda)) {}
391
Run()392 void Run() override {
393 MOZ_ASSERT(NS_IsMainThread());
394
395 JNIEnv* const env = jni::GetGeckoThreadEnv();
396 NPZCSupport* npzcSupport = GetNative(
397 java::PanZoomController::NativeProvider::LocalRef(env, mNPZC));
398
399 if (!npzcSupport || !npzcSupport->mWindow) {
400 // We already shut down.
401 env->ExceptionClear();
402 return;
403 }
404
405 nsWindow* const window = npzcSupport->mWindow;
406 window->UserActivity();
407 return mLambda(window);
408 }
409
IsUIEvent() const410 bool IsUIEvent() const override { return true; }
411 };
412
413 template <typename Lambda>
PostInputEvent(Lambda && aLambda)414 void PostInputEvent(Lambda&& aLambda) {
415 // Use priority queue for input events.
416 nsAppShell::PostEvent(
417 MakeUnique<InputEvent<Lambda>>(this, std::move(aLambda)));
418 }
419
420 public:
421 typedef java::PanZoomController::NativeProvider::Natives<NPZCSupport> Base;
422
NPZCSupport(NativePtr<NPZCSupport> * aPtr,nsWindow * aWindow,const java::PanZoomController::NativeProvider::LocalRef & aNPZC)423 NPZCSupport(NativePtr<NPZCSupport>* aPtr, nsWindow* aWindow,
424 const java::PanZoomController::NativeProvider::LocalRef& aNPZC)
425 : mWindow(aPtr, aWindow), mNPZC(aNPZC), mPreviousButtons(0) {
426 MOZ_ASSERT(mWindow);
427 }
428
~NPZCSupport()429 ~NPZCSupport() {}
430
431 using Base::AttachNative;
432 using Base::DisposeNative;
433
OnDetach(already_AddRefed<Runnable> aDisposer)434 void OnDetach(already_AddRefed<Runnable> aDisposer) {
435 RefPtr<Runnable> disposer = aDisposer;
436 // There are several considerations when shutting down NPZC. 1) The
437 // Gecko thread may destroy NPZC at any time when nsWindow closes. 2)
438 // There may be pending events on the Gecko thread when NPZC is
439 // destroyed. 3) mWindow may not be available when the pending event
440 // runs. 4) The UI thread may destroy NPZC at any time when GeckoView
441 // is destroyed. 5) The UI thread may destroy NPZC at the same time as
442 // Gecko thread trying to destroy NPZC. 6) There may be pending calls
443 // on the UI thread when NPZC is destroyed. 7) mWindow may have been
444 // cleared on the Gecko thread when the pending call happens on the UI
445 // thread.
446 //
447 // 1) happens through OnDetach, which first notifies the UI
448 // thread through Destroy; Destroy then calls DisposeNative, which
449 // finally disposes the native instance back on the Gecko thread. Using
450 // Destroy to indirectly call DisposeNative here also solves 5), by
451 // making everything go through the UI thread, avoiding contention.
452 //
453 // 2) and 3) are solved by clearing mWindow, which signals to the
454 // pending event that we had shut down. In that case the event bails
455 // and does not touch mWindow.
456 //
457 // 4) happens through DisposeNative directly. OnDetach is not
458 // called.
459 //
460 // 6) is solved by keeping a destroyed flag in the Java NPZC instance,
461 // and only make a pending call if the destroyed flag is not set.
462 //
463 // 7) is solved by taking a lock whenever mWindow is modified on the
464 // Gecko thread or accessed on the UI thread. That way, we don't
465 // release mWindow until the UI thread is done using it, thus avoiding
466 // the race condition.
467
468 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
469 auto npzc = java::PanZoomController::NativeProvider::GlobalRef(mNPZC);
470 if (!npzc) {
471 return;
472 }
473
474 uiThread->Dispatch(NS_NewRunnableFunction(
475 "NPZCSupport::OnDetach", [npzc, disposer = std::move(disposer)] {
476 npzc->SetAttached(false);
477 disposer->Run();
478 }));
479 }
480 }
481
GetJavaNPZC() const482 const java::PanZoomController::NativeProvider::Ref& GetJavaNPZC() const {
483 return mNPZC;
484 }
485
486 public:
SetIsLongpressEnabled(bool aIsLongpressEnabled)487 void SetIsLongpressEnabled(bool aIsLongpressEnabled) {
488 RefPtr<IAPZCTreeManager> controller;
489
490 if (LockedWindowPtr window{mWindow}) {
491 controller = window->mAPZC;
492 }
493
494 if (controller) {
495 controller->SetLongTapEnabled(aIsLongpressEnabled);
496 }
497 }
498
HandleScrollEvent(int64_t aTime,int32_t aMetaState,float aX,float aY,float aHScroll,float aVScroll)499 int32_t HandleScrollEvent(int64_t aTime, int32_t aMetaState, float aX,
500 float aY, float aHScroll, float aVScroll) {
501 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
502
503 RefPtr<IAPZCTreeManager> controller;
504
505 if (LockedWindowPtr window{mWindow}) {
506 controller = window->mAPZC;
507 }
508
509 if (!controller) {
510 return INPUT_RESULT_UNHANDLED;
511 }
512
513 ScreenPoint origin = ScreenPoint(aX, aY);
514
515 if (StaticPrefs::ui_scrolling_negate_wheel_scroll()) {
516 aHScroll = -aHScroll;
517 aVScroll = -aVScroll;
518 }
519
520 ScrollWheelInput input(
521 aTime, GetEventTimeStamp(aTime), GetModifiers(aMetaState),
522 ScrollWheelInput::SCROLLMODE_SMOOTH,
523 ScrollWheelInput::SCROLLDELTA_PIXEL, origin, aHScroll, aVScroll, false,
524 // XXX Do we need to support auto-dir scrolling
525 // for Android widgets with a wheel device?
526 // Currently, I just leave it unimplemented. If
527 // we need to implement it, what's the extra work
528 // to do?
529 WheelDeltaAdjustmentStrategy::eNone);
530
531 APZEventResult result = controller->InputBridge()->ReceiveInputEvent(input);
532 int32_t ret =
533 !result.mTargetIsRoot || result.mHitRegionWithApzAwareListeners
534 ? INPUT_RESULT_HANDLED_CONTENT
535 : INPUT_RESULT_HANDLED;
536
537 if (result.mStatus == nsEventStatus_eConsumeNoDefault) {
538 return ret;
539 }
540
541 PostInputEvent([input, result](nsWindow* window) {
542 WidgetWheelEvent wheelEvent = input.ToWidgetWheelEvent(window);
543 window->ProcessUntransformedAPZEvent(&wheelEvent, result);
544 });
545
546 switch (result.mStatus) {
547 case nsEventStatus_eIgnore:
548 return INPUT_RESULT_UNHANDLED;
549 case nsEventStatus_eConsumeDoDefault:
550 return ret;
551 default:
552 MOZ_ASSERT_UNREACHABLE("Unexpected nsEventStatus");
553 return INPUT_RESULT_UNHANDLED;
554 }
555 }
556
557 private:
GetButtonType(int button)558 static MouseInput::ButtonType GetButtonType(int button) {
559 MouseInput::ButtonType result = MouseInput::NONE;
560
561 switch (button) {
562 case java::sdk::MotionEvent::BUTTON_PRIMARY:
563 result = MouseInput::LEFT_BUTTON;
564 break;
565 case java::sdk::MotionEvent::BUTTON_SECONDARY:
566 result = MouseInput::RIGHT_BUTTON;
567 break;
568 case java::sdk::MotionEvent::BUTTON_TERTIARY:
569 result = MouseInput::MIDDLE_BUTTON;
570 break;
571 default:
572 break;
573 }
574
575 return result;
576 }
577
ConvertButtons(int buttons)578 static int16_t ConvertButtons(int buttons) {
579 int16_t result = 0;
580
581 if (buttons & java::sdk::MotionEvent::BUTTON_PRIMARY) {
582 result |= MouseButtonsFlag::eLeftFlag;
583 }
584 if (buttons & java::sdk::MotionEvent::BUTTON_SECONDARY) {
585 result |= MouseButtonsFlag::eRightFlag;
586 }
587 if (buttons & java::sdk::MotionEvent::BUTTON_TERTIARY) {
588 result |= MouseButtonsFlag::eMiddleFlag;
589 }
590 if (buttons & java::sdk::MotionEvent::BUTTON_BACK) {
591 result |= MouseButtonsFlag::e4thFlag;
592 }
593 if (buttons & java::sdk::MotionEvent::BUTTON_FORWARD) {
594 result |= MouseButtonsFlag::e5thFlag;
595 }
596
597 return result;
598 }
599
600 public:
HandleMouseEvent(int32_t aAction,int64_t aTime,int32_t aMetaState,float aX,float aY,int buttons)601 int32_t HandleMouseEvent(int32_t aAction, int64_t aTime, int32_t aMetaState,
602 float aX, float aY, int buttons) {
603 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
604
605 RefPtr<IAPZCTreeManager> controller;
606
607 if (LockedWindowPtr window{mWindow}) {
608 controller = window->mAPZC;
609 }
610
611 if (!controller) {
612 return INPUT_RESULT_UNHANDLED;
613 }
614
615 MouseInput::MouseType mouseType = MouseInput::MOUSE_NONE;
616 MouseInput::ButtonType buttonType = MouseInput::NONE;
617 switch (aAction) {
618 case java::sdk::MotionEvent::ACTION_DOWN:
619 mouseType = MouseInput::MOUSE_DOWN;
620 buttonType = GetButtonType(buttons ^ mPreviousButtons);
621 mPreviousButtons = buttons;
622 break;
623 case java::sdk::MotionEvent::ACTION_UP:
624 mouseType = MouseInput::MOUSE_UP;
625 buttonType = GetButtonType(buttons ^ mPreviousButtons);
626 mPreviousButtons = buttons;
627 break;
628 case java::sdk::MotionEvent::ACTION_MOVE:
629 mouseType = MouseInput::MOUSE_MOVE;
630 break;
631 case java::sdk::MotionEvent::ACTION_HOVER_MOVE:
632 mouseType = MouseInput::MOUSE_MOVE;
633 break;
634 case java::sdk::MotionEvent::ACTION_HOVER_ENTER:
635 mouseType = MouseInput::MOUSE_WIDGET_ENTER;
636 break;
637 case java::sdk::MotionEvent::ACTION_HOVER_EXIT:
638 mouseType = MouseInput::MOUSE_WIDGET_EXIT;
639 break;
640 default:
641 break;
642 }
643
644 if (mouseType == MouseInput::MOUSE_NONE) {
645 return INPUT_RESULT_UNHANDLED;
646 }
647
648 ScreenPoint origin = ScreenPoint(aX, aY);
649
650 MouseInput input(mouseType, buttonType,
651 MouseEvent_Binding::MOZ_SOURCE_MOUSE,
652 ConvertButtons(buttons), origin, aTime,
653 GetEventTimeStamp(aTime), GetModifiers(aMetaState));
654
655 APZEventResult result = controller->InputBridge()->ReceiveInputEvent(input);
656 int32_t ret =
657 !result.mTargetIsRoot || result.mHitRegionWithApzAwareListeners
658 ? INPUT_RESULT_HANDLED_CONTENT
659 : INPUT_RESULT_HANDLED;
660
661 if (result.mStatus == nsEventStatus_eConsumeNoDefault) {
662 return ret;
663 }
664
665 PostInputEvent([input, result](nsWindow* window) {
666 WidgetMouseEvent mouseEvent = input.ToWidgetMouseEvent(window);
667 window->ProcessUntransformedAPZEvent(&mouseEvent, result);
668 });
669
670 switch (result.mStatus) {
671 case nsEventStatus_eIgnore:
672 return INPUT_RESULT_UNHANDLED;
673 case nsEventStatus_eConsumeDoDefault:
674 return ret;
675 default:
676 MOZ_ASSERT_UNREACHABLE("Unexpected nsEventStatus");
677 return INPUT_RESULT_UNHANDLED;
678 }
679 }
680
HandleMotionEvent(const java::PanZoomController::NativeProvider::LocalRef & aInstance,int32_t aAction,int32_t aActionIndex,int64_t aTime,int32_t aMetaState,float aScreenX,float aScreenY,jni::IntArray::Param aPointerId,jni::FloatArray::Param aX,jni::FloatArray::Param aY,jni::FloatArray::Param aOrientation,jni::FloatArray::Param aPressure,jni::FloatArray::Param aToolMajor,jni::FloatArray::Param aToolMinor)681 int32_t HandleMotionEvent(
682 const java::PanZoomController::NativeProvider::LocalRef& aInstance,
683 int32_t aAction, int32_t aActionIndex, int64_t aTime, int32_t aMetaState,
684 float aScreenX, float aScreenY, jni::IntArray::Param aPointerId,
685 jni::FloatArray::Param aX, jni::FloatArray::Param aY,
686 jni::FloatArray::Param aOrientation, jni::FloatArray::Param aPressure,
687 jni::FloatArray::Param aToolMajor, jni::FloatArray::Param aToolMinor) {
688 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
689
690 RefPtr<IAPZCTreeManager> controller;
691
692 if (LockedWindowPtr window{mWindow}) {
693 controller = window->mAPZC;
694 }
695
696 if (!controller) {
697 return INPUT_RESULT_UNHANDLED;
698 }
699
700 nsTArray<int32_t> pointerId(aPointerId->GetElements());
701 MultiTouchInput::MultiTouchType type;
702 size_t startIndex = 0;
703 size_t endIndex = pointerId.Length();
704
705 switch (aAction) {
706 case java::sdk::MotionEvent::ACTION_DOWN:
707 case java::sdk::MotionEvent::ACTION_POINTER_DOWN:
708 type = MultiTouchInput::MULTITOUCH_START;
709 break;
710 case java::sdk::MotionEvent::ACTION_MOVE:
711 type = MultiTouchInput::MULTITOUCH_MOVE;
712 break;
713 case java::sdk::MotionEvent::ACTION_UP:
714 case java::sdk::MotionEvent::ACTION_POINTER_UP:
715 // for pointer-up events we only want the data from
716 // the one pointer that went up
717 type = MultiTouchInput::MULTITOUCH_END;
718 startIndex = aActionIndex;
719 endIndex = aActionIndex + 1;
720 break;
721 case java::sdk::MotionEvent::ACTION_OUTSIDE:
722 case java::sdk::MotionEvent::ACTION_CANCEL:
723 type = MultiTouchInput::MULTITOUCH_CANCEL;
724 break;
725 default:
726 return INPUT_RESULT_UNHANDLED;
727 }
728
729 MultiTouchInput input(type, aTime, GetEventTimeStamp(aTime), 0);
730 input.modifiers = GetModifiers(aMetaState);
731 input.mTouches.SetCapacity(endIndex - startIndex);
732 input.mScreenOffset =
733 ExternalIntPoint(int32_t(floorf(aScreenX)), int32_t(floorf(aScreenY)));
734
735 nsTArray<float> x(aX->GetElements());
736 nsTArray<float> y(aY->GetElements());
737 nsTArray<float> orientation(aOrientation->GetElements());
738 nsTArray<float> pressure(aPressure->GetElements());
739 nsTArray<float> toolMajor(aToolMajor->GetElements());
740 nsTArray<float> toolMinor(aToolMinor->GetElements());
741
742 MOZ_ASSERT(pointerId.Length() == x.Length());
743 MOZ_ASSERT(pointerId.Length() == y.Length());
744 MOZ_ASSERT(pointerId.Length() == orientation.Length());
745 MOZ_ASSERT(pointerId.Length() == pressure.Length());
746 MOZ_ASSERT(pointerId.Length() == toolMajor.Length());
747 MOZ_ASSERT(pointerId.Length() == toolMinor.Length());
748
749 for (size_t i = startIndex; i < endIndex; i++) {
750 float orien = orientation[i] * 180.0f / M_PI;
751 // w3c touchevents spec does not allow orientations == 90
752 // this shifts it to -90, which will be shifted to zero below
753 if (orien >= 90.0) {
754 orien -= 180.0f;
755 }
756
757 nsIntPoint point =
758 nsIntPoint(int32_t(floorf(x[i])), int32_t(floorf(y[i])));
759
760 // w3c touchevent radii are given with an orientation between 0 and
761 // 90. The radii are found by removing the orientation and
762 // measuring the x and y radii of the resulting ellipse. For
763 // Android orientations >= 0 and < 90, use the y radius as the
764 // major radius, and x as the minor radius. However, for an
765 // orientation < 0, we have to shift the orientation by adding 90,
766 // and reverse which radius is major and minor.
767 gfx::Size radius;
768 if (orien < 0.0f) {
769 orien += 90.0f;
770 radius = gfx::Size(int32_t(toolMajor[i] / 2.0f),
771 int32_t(toolMinor[i] / 2.0f));
772 } else {
773 radius = gfx::Size(int32_t(toolMinor[i] / 2.0f),
774 int32_t(toolMajor[i] / 2.0f));
775 }
776
777 input.mTouches.AppendElement(SingleTouchData(
778 pointerId[i], ScreenIntPoint::FromUnknownPoint(point),
779 ScreenSize::FromUnknownSize(radius), orien, pressure[i]));
780 }
781
782 APZEventResult result = controller->InputBridge()->ReceiveInputEvent(input);
783 int32_t ret =
784 !result.mTargetIsRoot || result.mHitRegionWithApzAwareListeners
785 ? INPUT_RESULT_HANDLED_CONTENT
786 : INPUT_RESULT_HANDLED;
787
788 if (result.mStatus == nsEventStatus_eConsumeNoDefault) {
789 return ret;
790 }
791
792 // Dispatch APZ input event on Gecko thread.
793 PostInputEvent([input, result](nsWindow* window) {
794 WidgetTouchEvent touchEvent = input.ToWidgetTouchEvent(window);
795 window->ProcessUntransformedAPZEvent(&touchEvent, result);
796 window->DispatchHitTest(touchEvent);
797 });
798
799 switch (result.mStatus) {
800 case nsEventStatus_eIgnore:
801 return INPUT_RESULT_UNHANDLED;
802 case nsEventStatus_eConsumeDoDefault:
803 return ret;
804 default:
805 MOZ_ASSERT_UNREACHABLE("Unexpected nsEventStatus");
806 return INPUT_RESULT_UNHANDLED;
807 }
808 }
809 };
810
811 template <>
812 const char nsWindow::NativePtr<nsWindow::NPZCSupport>::sName[] = "NPZCSupport";
813
NS_IMPL_ISUPPORTS(nsWindow::AndroidView,nsIAndroidEventDispatcher,nsIAndroidView)814 NS_IMPL_ISUPPORTS(nsWindow::AndroidView, nsIAndroidEventDispatcher,
815 nsIAndroidView)
816
817 nsresult nsWindow::AndroidView::GetInitData(JSContext* aCx,
818 JS::MutableHandleValue aOut) {
819 if (!mInitData) {
820 aOut.setNull();
821 return NS_OK;
822 }
823
824 return widget::EventDispatcher::UnboxBundle(aCx, mInitData, aOut);
825 }
826
827 /**
828 * Compositor has some unique requirements for its native calls, so make it
829 * separate from GeckoViewSupport.
830 */
831 class nsWindow::LayerViewSupport final
832 : public GeckoSession::Compositor::Natives<LayerViewSupport> {
833 using LockedWindowPtr = WindowPtr<LayerViewSupport>::Locked;
834
835 WindowPtr<LayerViewSupport> mWindow;
836 GeckoSession::Compositor::WeakRef mCompositor;
837 Atomic<bool, ReleaseAcquire> mCompositorPaused;
838 jni::Object::GlobalRef mSurface;
839
840 struct CaptureRequest {
CaptureRequestnsWindow::LayerViewSupport::CaptureRequest841 explicit CaptureRequest() : mResult(nullptr) {}
CaptureRequestnsWindow::LayerViewSupport::CaptureRequest842 explicit CaptureRequest(java::GeckoResult::GlobalRef aResult,
843 const ScreenRect& aSource,
844 const IntSize& aOutputSize)
845 : mResult(aResult), mSource(aSource), mOutputSize(aOutputSize) {}
846
847 // where to send the pixels
848 java::GeckoResult::GlobalRef mResult;
849
850 ScreenRect mSource;
851
852 IntSize mOutputSize;
853 };
854 std::queue<CaptureRequest> mCapturePixelsResults;
855
856 // In order to use Event::HasSameTypeAs in PostTo(), we cannot make
857 // LayerViewEvent a template because each template instantiation is
858 // a different type. So implement LayerViewEvent as a ProxyEvent.
859 class LayerViewEvent final : public nsAppShell::ProxyEvent {
860 using Event = nsAppShell::Event;
861
862 public:
MakeEvent(UniquePtr<Event> && event)863 static UniquePtr<Event> MakeEvent(UniquePtr<Event>&& event) {
864 return MakeUnique<LayerViewEvent>(std::move(event));
865 }
866
LayerViewEvent(UniquePtr<Event> && event)867 explicit LayerViewEvent(UniquePtr<Event>&& event)
868 : nsAppShell::ProxyEvent(std::move(event)) {}
869
PostTo(LinkedList<Event> & queue)870 void PostTo(LinkedList<Event>& queue) override {
871 // Give priority to compositor events, but keep in order with
872 // existing compositor events.
873 nsAppShell::Event* event = queue.getFirst();
874 while (event && event->HasSameTypeAs(this)) {
875 event = event->getNext();
876 }
877 if (event) {
878 event->setPrevious(this);
879 } else {
880 queue.insertBack(this);
881 }
882 }
883 };
884
885 public:
886 typedef GeckoSession::Compositor::Natives<LayerViewSupport> Base;
887
FromNative(const GeckoSession::Compositor::LocalRef & instance)888 static LayerViewSupport* FromNative(
889 const GeckoSession::Compositor::LocalRef& instance) {
890 return GetNative(instance);
891 }
892
LayerViewSupport(NativePtr<LayerViewSupport> * aPtr,nsWindow * aWindow,const GeckoSession::Compositor::LocalRef & aInstance)893 LayerViewSupport(NativePtr<LayerViewSupport>* aPtr, nsWindow* aWindow,
894 const GeckoSession::Compositor::LocalRef& aInstance)
895 : mWindow(aPtr, aWindow),
896 mCompositor(aInstance),
897 mCompositorPaused(true) {
898 MOZ_ASSERT(mWindow);
899 }
900
~LayerViewSupport()901 ~LayerViewSupport() {}
902
903 using Base::AttachNative;
904 using Base::DisposeNative;
905
OnDetach(already_AddRefed<Runnable> aDisposer)906 void OnDetach(already_AddRefed<Runnable> aDisposer) {
907 RefPtr<Runnable> disposer = aDisposer;
908 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
909 GeckoSession::Compositor::GlobalRef compositor(mCompositor);
910 if (!compositor) {
911 return;
912 }
913
914 uiThread->Dispatch(NS_NewRunnableFunction(
915 "LayerViewSupport::OnDetach",
916 [compositor, disposer = std::move(disposer),
917 results = &mCapturePixelsResults, window = &mWindow] {
918 if (LockedWindowPtr lock{*window}) {
919 while (!results->empty()) {
920 auto aResult =
921 java::GeckoResult::LocalRef(results->front().mResult);
922 if (aResult) {
923 aResult->CompleteExceptionally(
924 java::sdk::IllegalStateException::New(
925 "The compositor has detached from the session")
926 .Cast<jni::Throwable>());
927 results->pop();
928 }
929 }
930 compositor->OnCompositorDetached();
931 disposer->Run();
932 }
933 }));
934 }
935 }
936
GetJavaCompositor() const937 const GeckoSession::Compositor::Ref& GetJavaCompositor() const {
938 return mCompositor;
939 }
940
CompositorPaused() const941 bool CompositorPaused() const { return mCompositorPaused; }
942
GetSurface()943 jni::Object::Param GetSurface() { return mSurface; }
944
945 private:
946 already_AddRefed<UiCompositorControllerChild>
GetUiCompositorControllerChild()947 GetUiCompositorControllerChild() {
948 RefPtr<UiCompositorControllerChild> child;
949 if (LockedWindowPtr window{mWindow}) {
950 child = window->GetUiCompositorControllerChild();
951 }
952 return child.forget();
953 }
954
FlipScreenPixels(Shmem & aMem,const ScreenIntSize & aInSize,const ScreenRect & aInRegion,const IntSize & aOutSize)955 int8_t* FlipScreenPixels(Shmem& aMem, const ScreenIntSize& aInSize,
956 const ScreenRect& aInRegion,
957 const IntSize& aOutSize) {
958 RefPtr<DataSourceSurface> image = gfx::CreateDataSourceSurfaceFromData(
959 IntSize(aInSize.width, aInSize.height), SurfaceFormat::B8G8R8A8,
960 aMem.get<uint8_t>(),
961 StrideForFormatAndWidth(SurfaceFormat::B8G8R8A8, aInSize.width));
962 RefPtr<DrawTarget> drawTarget =
963 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
964 aOutSize, SurfaceFormat::B8G8R8A8);
965 drawTarget->SetTransform(Matrix::Scaling(1.0, -1.0) *
966 Matrix::Translation(0, aOutSize.height));
967
968 gfx::Rect srcRect(aInRegion.x,
969 (aInSize.height - aInRegion.height) - aInRegion.y,
970 aInRegion.width, aInRegion.height);
971 gfx::Rect destRect(0, 0, aOutSize.width, aOutSize.height);
972 drawTarget->DrawSurface(image, destRect, srcRect);
973
974 RefPtr<SourceSurface> snapshot = drawTarget->Snapshot();
975 RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
976 DataSourceSurface::ScopedMap* smap =
977 new DataSourceSurface::ScopedMap(data, DataSourceSurface::READ);
978 return reinterpret_cast<int8_t*>(smap->GetData());
979 }
980
981 /**
982 * Compositor methods
983 */
984 public:
AttachNPZC(jni::Object::Param aNPZC)985 void AttachNPZC(jni::Object::Param aNPZC) {
986 MOZ_ASSERT(NS_IsMainThread());
987 if (!mWindow) {
988 return; // Already shut down.
989 }
990
991 MOZ_ASSERT(aNPZC);
992
993 // We can have this situation if we get two GeckoViewSupport::Transfer()
994 // called before the first AttachNPZC() gets here. Just detach the current
995 // instance since that's what happens in GeckoViewSupport::Transfer() as
996 // well.
997 if (mWindow->mNPZCSupport) {
998 mWindow->mNPZCSupport.Detach(mWindow->mNPZCSupport->GetJavaNPZC());
999 }
1000
1001 auto npzc = java::PanZoomController::NativeProvider::LocalRef(
1002 jni::GetGeckoThreadEnv(),
1003 java::PanZoomController::NativeProvider::Ref::From(aNPZC));
1004 mWindow->mNPZCSupport.Attach(npzc, mWindow, npzc);
1005
1006 DispatchToUiThread(
1007 "LayerViewSupport::AttachNPZC",
1008 [npzc = java::PanZoomController::NativeProvider::GlobalRef(npzc)] {
1009 npzc->SetAttached(true);
1010 });
1011 }
1012
OnBoundsChanged(int32_t aLeft,int32_t aTop,int32_t aWidth,int32_t aHeight)1013 void OnBoundsChanged(int32_t aLeft, int32_t aTop, int32_t aWidth,
1014 int32_t aHeight) {
1015 MOZ_ASSERT(NS_IsMainThread());
1016 if (!mWindow) {
1017 return; // Already shut down.
1018 }
1019
1020 mWindow->Resize(aLeft, aTop, aWidth, aHeight, /* repaint */ false);
1021 }
1022
SetDynamicToolbarMaxHeight(int32_t aHeight)1023 void SetDynamicToolbarMaxHeight(int32_t aHeight) {
1024 MOZ_ASSERT(NS_IsMainThread());
1025 if (!mWindow) {
1026 return; // Already shut down.
1027 }
1028
1029 mWindow->UpdateDynamicToolbarMaxHeight(ScreenIntCoord(aHeight));
1030 }
1031
SyncPauseCompositor()1032 void SyncPauseCompositor() {
1033 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1034
1035 if (RefPtr<UiCompositorControllerChild> child =
1036 GetUiCompositorControllerChild()) {
1037 mCompositorPaused = true;
1038 child->Pause();
1039 }
1040 }
1041
SyncResumeCompositor()1042 void SyncResumeCompositor() {
1043 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1044
1045 if (RefPtr<UiCompositorControllerChild> child =
1046 GetUiCompositorControllerChild()) {
1047 mCompositorPaused = false;
1048 child->Resume();
1049 }
1050 }
1051
SyncResumeResizeCompositor(const GeckoSession::Compositor::LocalRef & aObj,int32_t aX,int32_t aY,int32_t aWidth,int32_t aHeight,jni::Object::Param aSurface)1052 void SyncResumeResizeCompositor(
1053 const GeckoSession::Compositor::LocalRef& aObj, int32_t aX, int32_t aY,
1054 int32_t aWidth, int32_t aHeight, jni::Object::Param aSurface) {
1055 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1056
1057 mSurface = aSurface;
1058
1059 if (RefPtr<UiCompositorControllerChild> child =
1060 GetUiCompositorControllerChild()) {
1061 child->ResumeAndResize(aX, aY, aWidth, aHeight);
1062 }
1063
1064 mCompositorPaused = false;
1065
1066 class OnResumedEvent : public nsAppShell::Event {
1067 GeckoSession::Compositor::GlobalRef mCompositor;
1068
1069 public:
1070 explicit OnResumedEvent(GeckoSession::Compositor::GlobalRef&& aCompositor)
1071 : mCompositor(std::move(aCompositor)) {}
1072
1073 void Run() override {
1074 MOZ_ASSERT(NS_IsMainThread());
1075
1076 JNIEnv* const env = jni::GetGeckoThreadEnv();
1077 LayerViewSupport* const lvs =
1078 GetNative(GeckoSession::Compositor::LocalRef(env, mCompositor));
1079
1080 if (!lvs || !lvs->mWindow) {
1081 env->ExceptionClear();
1082 return; // Already shut down.
1083 }
1084
1085 // When we get here, the compositor has already been told to
1086 // resume. This means it's now safe for layer updates to occur.
1087 // Since we might have prevented one or more draw events from
1088 // occurring while the compositor was paused, we need to
1089 // schedule a draw event now.
1090 if (!lvs->mCompositorPaused) {
1091 lvs->mWindow->RedrawAll();
1092 }
1093 }
1094 };
1095
1096 // Use priority queue for timing-sensitive event.
1097 nsAppShell::PostEvent(
1098 MakeUnique<LayerViewEvent>(MakeUnique<OnResumedEvent>(aObj)));
1099 }
1100
SyncInvalidateAndScheduleComposite()1101 void SyncInvalidateAndScheduleComposite() {
1102 RefPtr<UiCompositorControllerChild> child =
1103 GetUiCompositorControllerChild();
1104 if (!child) {
1105 return;
1106 }
1107
1108 if (AndroidBridge::IsJavaUiThread()) {
1109 child->InvalidateAndRender();
1110 return;
1111 }
1112
1113 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
1114 uiThread->Dispatch(NewRunnableMethod<>(
1115 "LayerViewSupport::InvalidateAndRender", child,
1116 &UiCompositorControllerChild::InvalidateAndRender),
1117 nsIThread::DISPATCH_NORMAL);
1118 }
1119 }
1120
SetMaxToolbarHeight(int32_t aHeight)1121 void SetMaxToolbarHeight(int32_t aHeight) {
1122 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1123
1124 if (RefPtr<UiCompositorControllerChild> child =
1125 GetUiCompositorControllerChild()) {
1126 child->SetMaxToolbarHeight(aHeight);
1127 }
1128 }
1129
SetFixedBottomOffset(int32_t aOffset)1130 void SetFixedBottomOffset(int32_t aOffset) {
1131 if (mWindow) {
1132 mWindow->UpdateDynamicToolbarOffset(ScreenIntCoord(aOffset));
1133 }
1134
1135 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
1136 uiThread->Dispatch(NS_NewRunnableFunction(
1137 "LayerViewSupport::SetFixedBottomOffset", [this, offset = aOffset] {
1138 if (RefPtr<UiCompositorControllerChild> child =
1139 GetUiCompositorControllerChild()) {
1140 child->SetFixedBottomOffset(offset);
1141 }
1142 }));
1143 }
1144 }
1145
SendToolbarAnimatorMessage(int32_t aMessage)1146 void SendToolbarAnimatorMessage(int32_t aMessage) {
1147 RefPtr<UiCompositorControllerChild> child =
1148 GetUiCompositorControllerChild();
1149 if (!child) {
1150 return;
1151 }
1152
1153 if (AndroidBridge::IsJavaUiThread()) {
1154 child->ToolbarAnimatorMessageFromUI(aMessage);
1155 return;
1156 }
1157
1158 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
1159 uiThread->Dispatch(
1160 NewRunnableMethod<int32_t>(
1161 "LayerViewSupport::ToolbarAnimatorMessageFromUI", child,
1162 &UiCompositorControllerChild::ToolbarAnimatorMessageFromUI,
1163 aMessage),
1164 nsIThread::DISPATCH_NORMAL);
1165 }
1166 }
1167
RecvToolbarAnimatorMessage(int32_t aMessage)1168 void RecvToolbarAnimatorMessage(int32_t aMessage) {
1169 auto compositor = GeckoSession::Compositor::LocalRef(mCompositor);
1170 if (compositor) {
1171 compositor->RecvToolbarAnimatorMessage(aMessage);
1172 }
1173 }
1174
SetDefaultClearColor(int32_t aColor)1175 void SetDefaultClearColor(int32_t aColor) {
1176 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1177 if (RefPtr<UiCompositorControllerChild> child =
1178 GetUiCompositorControllerChild()) {
1179 child->SetDefaultClearColor((uint32_t)aColor);
1180 }
1181 }
1182
RequestScreenPixels(jni::Object::Param aResult,int32_t aXOffset,int32_t aYOffset,int32_t aSrcWidth,int32_t aSrcHeight,int32_t aOutWidth,int32_t aOutHeight)1183 void RequestScreenPixels(jni::Object::Param aResult, int32_t aXOffset,
1184 int32_t aYOffset, int32_t aSrcWidth,
1185 int32_t aSrcHeight, int32_t aOutWidth,
1186 int32_t aOutHeight) {
1187 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1188
1189 int size = 0;
1190 if (LockedWindowPtr window{mWindow}) {
1191 mCapturePixelsResults.push(CaptureRequest(
1192 java::GeckoResult::GlobalRef(java::GeckoResult::LocalRef(aResult)),
1193 ScreenRect(aXOffset, aYOffset, aSrcWidth, aSrcHeight),
1194 IntSize(aOutWidth, aOutHeight)));
1195 size = mCapturePixelsResults.size();
1196 }
1197
1198 if (size == 1) {
1199 if (RefPtr<UiCompositorControllerChild> child =
1200 GetUiCompositorControllerChild()) {
1201 child->RequestScreenPixels();
1202 }
1203 }
1204 }
1205
RecvScreenPixels(Shmem && aMem,const ScreenIntSize & aSize)1206 void RecvScreenPixels(Shmem&& aMem, const ScreenIntSize& aSize) {
1207 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1208 CaptureRequest aCaptureRequest;
1209 java::GeckoResult::LocalRef aResult = nullptr;
1210 if (LockedWindowPtr window{mWindow}) {
1211 aCaptureRequest = mCapturePixelsResults.front();
1212 aResult = java::GeckoResult::LocalRef(aCaptureRequest.mResult);
1213 if (aResult) {
1214 mCapturePixelsResults.pop();
1215 }
1216 }
1217
1218 if (aResult) {
1219 auto pixels = mozilla::jni::ByteBuffer::New(
1220 FlipScreenPixels(aMem, aSize, aCaptureRequest.mSource,
1221 aCaptureRequest.mOutputSize),
1222 aMem.Size<int8_t>());
1223 aResult->Complete(pixels);
1224 }
1225
1226 // Pixels have been copied, so Dealloc Shmem
1227 if (RefPtr<UiCompositorControllerChild> child =
1228 GetUiCompositorControllerChild()) {
1229 child->DeallocPixelBuffer(aMem);
1230
1231 if (LockedWindowPtr window{mWindow}) {
1232 if (!mCapturePixelsResults.empty()) {
1233 child->RequestScreenPixels();
1234 }
1235 }
1236 }
1237 }
1238
EnableLayerUpdateNotifications(bool aEnable)1239 void EnableLayerUpdateNotifications(bool aEnable) {
1240 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1241 if (RefPtr<UiCompositorControllerChild> child =
1242 GetUiCompositorControllerChild()) {
1243 child->EnableLayerUpdateNotifications(aEnable);
1244 }
1245 }
1246
OnSafeAreaInsetsChanged(int32_t aTop,int32_t aRight,int32_t aBottom,int32_t aLeft)1247 void OnSafeAreaInsetsChanged(int32_t aTop, int32_t aRight, int32_t aBottom,
1248 int32_t aLeft) {
1249 MOZ_ASSERT(NS_IsMainThread());
1250 if (!mWindow) {
1251 return; // Already shut down.
1252 }
1253 ScreenIntMargin safeAreaInsets(aTop, aRight, aBottom, aLeft);
1254 mWindow->UpdateSafeAreaInsets(safeAreaInsets);
1255 }
1256 };
1257
1258 template <>
1259 const char nsWindow::NativePtr<nsWindow::LayerViewSupport>::sName[] =
1260 "LayerViewSupport";
1261
~GeckoViewSupport()1262 nsWindow::GeckoViewSupport::~GeckoViewSupport() {
1263 // Disassociate our GeckoEditable instance with our native object.
1264 if (window.mEditableSupport) {
1265 window.mEditableSupport.Detach(window.mEditableSupport->GetJavaEditable());
1266 window.mEditableParent = nullptr;
1267 }
1268
1269 if (window.mNPZCSupport) {
1270 window.mNPZCSupport.Detach(window.mNPZCSupport->GetJavaNPZC());
1271 }
1272
1273 if (window.mLayerViewSupport) {
1274 window.mLayerViewSupport.Detach(
1275 window.mLayerViewSupport->GetJavaCompositor());
1276 }
1277
1278 if (window.mSessionAccessibility) {
1279 window.mSessionAccessibility.Detach(
1280 window.mSessionAccessibility->GetJavaAccessibility());
1281 }
1282 }
1283
1284 /* static */
Open(const jni::Class::LocalRef & aCls,GeckoSession::Window::Param aWindow,jni::Object::Param aQueue,jni::Object::Param aCompositor,jni::Object::Param aDispatcher,jni::Object::Param aSessionAccessibility,jni::Object::Param aInitData,jni::String::Param aId,jni::String::Param aChromeURI,int32_t aScreenId,bool aPrivateMode,bool aRemote)1285 void nsWindow::GeckoViewSupport::Open(
1286 const jni::Class::LocalRef& aCls, GeckoSession::Window::Param aWindow,
1287 jni::Object::Param aQueue, jni::Object::Param aCompositor,
1288 jni::Object::Param aDispatcher, jni::Object::Param aSessionAccessibility,
1289 jni::Object::Param aInitData, jni::String::Param aId,
1290 jni::String::Param aChromeURI, int32_t aScreenId, bool aPrivateMode,
1291 bool aRemote) {
1292 MOZ_ASSERT(NS_IsMainThread());
1293
1294 AUTO_PROFILER_LABEL("nsWindow::GeckoViewSupport::Open", OTHER);
1295
1296 nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID);
1297 MOZ_RELEASE_ASSERT(ww);
1298
1299 nsAutoCString url;
1300 if (aChromeURI) {
1301 url = aChromeURI->ToCString();
1302 } else {
1303 nsresult rv = Preferences::GetCString("toolkit.defaultChromeURI", url);
1304 if (NS_FAILED(rv)) {
1305 url = NS_LITERAL_CSTRING("chrome://geckoview/content/geckoview.xhtml");
1306 }
1307 }
1308
1309 // Prepare an nsIAndroidView to pass as argument to the window.
1310 RefPtr<AndroidView> androidView = new AndroidView();
1311 androidView->mEventDispatcher->Attach(
1312 java::EventDispatcher::Ref::From(aDispatcher), nullptr);
1313 androidView->mInitData = java::GeckoBundle::Ref::From(aInitData);
1314
1315 nsAutoCString chromeFlags("chrome,dialog=0,resizable,scrollbars");
1316 if (aPrivateMode) {
1317 chromeFlags += ",private";
1318 }
1319 if (aRemote) {
1320 chromeFlags += ",remote";
1321 }
1322 nsCOMPtr<mozIDOMWindowProxy> domWindow;
1323 ww->OpenWindow(nullptr, url.get(), aId->ToCString().get(), chromeFlags.get(),
1324 androidView, getter_AddRefs(domWindow));
1325 MOZ_RELEASE_ASSERT(domWindow);
1326
1327 nsCOMPtr<nsPIDOMWindowOuter> pdomWindow = nsPIDOMWindowOuter::From(domWindow);
1328 const RefPtr<nsWindow> window = nsWindow::From(pdomWindow);
1329 MOZ_ASSERT(window);
1330 window->SetScreenId(aScreenId);
1331
1332 // Attach a new GeckoView support object to the new window.
1333 GeckoSession::Window::LocalRef sessionWindow(aCls.Env(), aWindow);
1334 window->mGeckoViewSupport =
1335 mozilla::MakeUnique<GeckoViewSupport>(window, sessionWindow);
1336 window->mGeckoViewSupport->mDOMWindow = pdomWindow;
1337 window->mAndroidView = androidView;
1338
1339 // Attach other session support objects.
1340 window->mGeckoViewSupport->Transfer(sessionWindow, aQueue, aCompositor,
1341 aDispatcher, aSessionAccessibility,
1342 aInitData);
1343
1344 if (window->mWidgetListener) {
1345 nsCOMPtr<nsIAppWindow> appWindow(window->mWidgetListener->GetAppWindow());
1346 if (appWindow) {
1347 // Our window is not intrinsically sized, so tell AppWindow to
1348 // not set a size for us.
1349 appWindow->SetIntrinsicallySized(false);
1350 }
1351 }
1352 }
1353
Close()1354 void nsWindow::GeckoViewSupport::Close() {
1355 if (window.mAndroidView) {
1356 window.mAndroidView->mEventDispatcher->Detach();
1357 }
1358
1359 if (!mDOMWindow) {
1360 return;
1361 }
1362
1363 mDOMWindow->ForceClose();
1364 mDOMWindow = nullptr;
1365 mGeckoViewWindow = nullptr;
1366 }
1367
Transfer(const GeckoSession::Window::LocalRef & inst,jni::Object::Param aQueue,jni::Object::Param aCompositor,jni::Object::Param aDispatcher,jni::Object::Param aSessionAccessibility,jni::Object::Param aInitData)1368 void nsWindow::GeckoViewSupport::Transfer(
1369 const GeckoSession::Window::LocalRef& inst, jni::Object::Param aQueue,
1370 jni::Object::Param aCompositor, jni::Object::Param aDispatcher,
1371 jni::Object::Param aSessionAccessibility, jni::Object::Param aInitData) {
1372 if (window.mNPZCSupport) {
1373 MOZ_ASSERT(window.mLayerViewSupport);
1374 window.mNPZCSupport.Detach(window.mNPZCSupport->GetJavaNPZC());
1375 }
1376
1377 auto compositor = GeckoSession::Compositor::LocalRef(
1378 inst.Env(), GeckoSession::Compositor::Ref::From(aCompositor));
1379 if (window.mLayerViewSupport &&
1380 window.mLayerViewSupport->GetJavaCompositor() != compositor) {
1381 window.mLayerViewSupport.Detach(
1382 window.mLayerViewSupport->GetJavaCompositor());
1383 }
1384 if (!window.mLayerViewSupport) {
1385 window.mLayerViewSupport.Attach(compositor, &window, compositor);
1386 }
1387
1388 MOZ_ASSERT(window.mAndroidView);
1389 window.mAndroidView->mEventDispatcher->Attach(
1390 java::EventDispatcher::Ref::From(aDispatcher), mDOMWindow);
1391
1392 if (window.mSessionAccessibility) {
1393 window.mSessionAccessibility.Detach(
1394 window.mSessionAccessibility->GetJavaAccessibility());
1395 }
1396 if (aSessionAccessibility) {
1397 AttachAccessibility(inst, aSessionAccessibility);
1398 }
1399
1400 if (mIsReady) {
1401 // We're in a transfer; update init-data and notify JS code.
1402 window.mAndroidView->mInitData = java::GeckoBundle::Ref::From(aInitData);
1403 OnReady(aQueue);
1404 window.mAndroidView->mEventDispatcher->Dispatch(
1405 u"GeckoView:UpdateInitData");
1406 }
1407
1408 DispatchToUiThread("GeckoViewSupport::Transfer",
1409 [compositor = GeckoSession::Compositor::GlobalRef(
1410 compositor)] { compositor->OnCompositorAttached(); });
1411 }
1412
AttachEditable(const GeckoSession::Window::LocalRef & inst,jni::Object::Param aEditableParent)1413 void nsWindow::GeckoViewSupport::AttachEditable(
1414 const GeckoSession::Window::LocalRef& inst,
1415 jni::Object::Param aEditableParent) {
1416 if (!window.mEditableSupport) {
1417 auto editableChild = java::GeckoEditableChild::New(aEditableParent,
1418 /* default */ true);
1419 window.mEditableSupport.Attach(editableChild, &window, editableChild);
1420 } else {
1421 window.mEditableSupport->TransferParent(aEditableParent);
1422 }
1423
1424 window.mEditableParent = aEditableParent;
1425 }
1426
AttachAccessibility(const GeckoSession::Window::LocalRef & inst,jni::Object::Param aSessionAccessibility)1427 void nsWindow::GeckoViewSupport::AttachAccessibility(
1428 const GeckoSession::Window::LocalRef& inst,
1429 jni::Object::Param aSessionAccessibility) {
1430 MOZ_ASSERT(!window.mSessionAccessibility);
1431 java::SessionAccessibility::NativeProvider::LocalRef sessionAccessibility(
1432 inst.Env());
1433 sessionAccessibility = java::SessionAccessibility::NativeProvider::Ref::From(
1434 aSessionAccessibility);
1435
1436 if (window.mSessionAccessibility) {
1437 window.mSessionAccessibility.Detach(
1438 window.mSessionAccessibility->GetJavaAccessibility());
1439 }
1440
1441 window.mSessionAccessibility.Attach(sessionAccessibility, &window,
1442 sessionAccessibility);
1443 }
1444
InitNatives()1445 void nsWindow::InitNatives() {
1446 jni::InitConversionStatics();
1447 nsWindow::GeckoViewSupport::Base::Init();
1448 nsWindow::LayerViewSupport::Init();
1449 nsWindow::NPZCSupport::Init();
1450
1451 GeckoEditableSupport::Init();
1452 a11y::SessionAccessibility::Init();
1453 }
1454
1455 /* static */
From(nsPIDOMWindowOuter * aDOMWindow)1456 already_AddRefed<nsWindow> nsWindow::From(nsPIDOMWindowOuter* aDOMWindow) {
1457 nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(aDOMWindow);
1458 return From(widget);
1459 }
1460
1461 /* static */
From(nsIWidget * aWidget)1462 already_AddRefed<nsWindow> nsWindow::From(nsIWidget* aWidget) {
1463 // `widget` may be one of several different types in the parent
1464 // process, including the Android nsWindow, PuppetWidget, etc. To
1465 // ensure that the cast to the Android nsWindow is valid, we check that the
1466 // widget is a top-level window and that its NS_NATIVE_WIDGET value is
1467 // non-null, which is not the case for non-native widgets like
1468 // PuppetWidget.
1469 if (aWidget && aWidget->WindowType() == nsWindowType::eWindowType_toplevel &&
1470 aWidget->GetNativeData(NS_NATIVE_WIDGET) == aWidget) {
1471 RefPtr<nsWindow> window = static_cast<nsWindow*>(aWidget);
1472 return window.forget();
1473 }
1474 return nullptr;
1475 }
1476
TopWindow()1477 nsWindow* nsWindow::TopWindow() {
1478 if (!gTopLevelWindows.IsEmpty()) return gTopLevelWindows[0];
1479 return nullptr;
1480 }
1481
LogWindow(nsWindow * win,int index,int indent)1482 void nsWindow::LogWindow(nsWindow* win, int index, int indent) {
1483 #if defined(DEBUG) || defined(FORCE_ALOG)
1484 char spaces[] = " ";
1485 spaces[indent < 20 ? indent : 20] = 0;
1486 ALOG("%s [% 2d] 0x%p [parent 0x%p] [% 3d,% 3dx% 3d,% 3d] vis %d type %d",
1487 spaces, index, win, win->mParent, win->mBounds.x, win->mBounds.y,
1488 win->mBounds.width, win->mBounds.height, win->mIsVisible,
1489 win->mWindowType);
1490 #endif
1491 }
1492
DumpWindows()1493 void nsWindow::DumpWindows() { DumpWindows(gTopLevelWindows); }
1494
DumpWindows(const nsTArray<nsWindow * > & wins,int indent)1495 void nsWindow::DumpWindows(const nsTArray<nsWindow*>& wins, int indent) {
1496 for (uint32_t i = 0; i < wins.Length(); ++i) {
1497 nsWindow* w = wins[i];
1498 LogWindow(w, i, indent);
1499 DumpWindows(w->mChildren, indent + 1);
1500 }
1501 }
1502
nsWindow()1503 nsWindow::nsWindow()
1504 : mScreenId(0), // Use 0 (primary screen) as the default value.
1505 mIsVisible(false),
1506 mParent(nullptr),
1507 mDynamicToolbarMaxHeight(0),
1508 mIsFullScreen(false),
1509 mIsDisablingWebRender(false) {}
1510
~nsWindow()1511 nsWindow::~nsWindow() {
1512 gTopLevelWindows.RemoveElement(this);
1513 ALOG("nsWindow %p destructor", (void*)this);
1514 // The mCompositorSession should have been cleaned up in nsWindow::Destroy()
1515 // DestroyLayerManager() will call DestroyCompositor() which will crash if
1516 // called from nsBaseWidget destructor. See Bug 1392705
1517 MOZ_ASSERT(!mCompositorSession);
1518 }
1519
IsTopLevel()1520 bool nsWindow::IsTopLevel() {
1521 return mWindowType == eWindowType_toplevel ||
1522 mWindowType == eWindowType_dialog ||
1523 mWindowType == eWindowType_invisible;
1524 }
1525
Create(nsIWidget * aParent,nsNativeWidget aNativeParent,const LayoutDeviceIntRect & aRect,nsWidgetInitData * aInitData)1526 nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
1527 const LayoutDeviceIntRect& aRect,
1528 nsWidgetInitData* aInitData) {
1529 ALOG("nsWindow[%p]::Create %p [%d %d %d %d]", (void*)this, (void*)aParent,
1530 aRect.x, aRect.y, aRect.width, aRect.height);
1531
1532 nsWindow* parent = (nsWindow*)aParent;
1533 if (aNativeParent) {
1534 if (parent) {
1535 ALOG(
1536 "Ignoring native parent on Android window [%p], "
1537 "since parent was specified (%p %p)",
1538 (void*)this, (void*)aNativeParent, (void*)aParent);
1539 } else {
1540 parent = (nsWindow*)aNativeParent;
1541 }
1542 }
1543
1544 // A default size of 1x1 confuses MobileViewportManager, so
1545 // use 0x0 instead. This is also a little more fitting since
1546 // we don't yet have a surface yet (and therefore a valid size)
1547 // and 0x0 is usually recognized as invalid.
1548 LayoutDeviceIntRect rect = aRect;
1549 if (aRect.width == 1 && aRect.height == 1) {
1550 rect.width = 0;
1551 rect.height = 0;
1552 }
1553
1554 mBounds = rect;
1555
1556 BaseCreate(nullptr, aInitData);
1557
1558 NS_ASSERTION(IsTopLevel() || parent,
1559 "non-top-level window doesn't have a parent!");
1560
1561 if (IsTopLevel()) {
1562 gTopLevelWindows.AppendElement(this);
1563
1564 } else if (parent) {
1565 parent->mChildren.AppendElement(this);
1566 mParent = parent;
1567 }
1568
1569 CreateLayerManager();
1570
1571 #ifdef DEBUG_ANDROID_WIDGET
1572 DumpWindows();
1573 #endif
1574
1575 return NS_OK;
1576 }
1577
Destroy()1578 void nsWindow::Destroy() {
1579 nsBaseWidget::mOnDestroyCalled = true;
1580
1581 if (mGeckoViewSupport) {
1582 // Disassociate our native object with GeckoView.
1583 mGeckoViewSupport = nullptr;
1584 }
1585
1586 // Stuff below may release the last ref to this
1587 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
1588
1589 while (mChildren.Length()) {
1590 // why do we still have children?
1591 ALOG("### Warning: Destroying window %p and reparenting child %p to null!",
1592 (void*)this, (void*)mChildren[0]);
1593 mChildren[0]->SetParent(nullptr);
1594 }
1595
1596 // Ensure the compositor has been shutdown before this nsWindow is potentially
1597 // deleted
1598 nsBaseWidget::DestroyCompositor();
1599
1600 nsBaseWidget::Destroy();
1601
1602 if (IsTopLevel()) gTopLevelWindows.RemoveElement(this);
1603
1604 SetParent(nullptr);
1605
1606 nsBaseWidget::OnDestroy();
1607
1608 #ifdef DEBUG_ANDROID_WIDGET
1609 DumpWindows();
1610 #endif
1611 }
1612
ConfigureChildren(const nsTArray<nsIWidget::Configuration> & config)1613 nsresult nsWindow::ConfigureChildren(
1614 const nsTArray<nsIWidget::Configuration>& config) {
1615 for (uint32_t i = 0; i < config.Length(); ++i) {
1616 nsWindow* childWin = (nsWindow*)config[i].mChild.get();
1617 childWin->Resize(config[i].mBounds.x, config[i].mBounds.y,
1618 config[i].mBounds.width, config[i].mBounds.height, false);
1619 }
1620
1621 return NS_OK;
1622 }
1623
RedrawAll()1624 void nsWindow::RedrawAll() {
1625 if (mAttachedWidgetListener) {
1626 mAttachedWidgetListener->RequestRepaint();
1627 } else if (mWidgetListener) {
1628 mWidgetListener->RequestRepaint();
1629 }
1630 }
1631
GetUiCompositorControllerChild()1632 RefPtr<UiCompositorControllerChild> nsWindow::GetUiCompositorControllerChild() {
1633 return mCompositorSession
1634 ? mCompositorSession->GetUiCompositorControllerChild()
1635 : nullptr;
1636 }
1637
GetRootLayerId() const1638 mozilla::layers::LayersId nsWindow::GetRootLayerId() const {
1639 return mCompositorSession ? mCompositorSession->RootLayerTreeId()
1640 : mozilla::layers::LayersId{0};
1641 }
1642
OnGeckoViewReady()1643 void nsWindow::OnGeckoViewReady() {
1644 if (!mGeckoViewSupport) {
1645 return;
1646 }
1647 mGeckoViewSupport->OnReady();
1648 }
1649
SetParent(nsIWidget * aNewParent)1650 void nsWindow::SetParent(nsIWidget* aNewParent) {
1651 if ((nsIWidget*)mParent == aNewParent) return;
1652
1653 // If we had a parent before, remove ourselves from its list of
1654 // children.
1655 if (mParent) mParent->mChildren.RemoveElement(this);
1656
1657 mParent = (nsWindow*)aNewParent;
1658
1659 if (mParent) mParent->mChildren.AppendElement(this);
1660
1661 // if we are now in the toplevel window's hierarchy, schedule a redraw
1662 if (FindTopLevel() == nsWindow::TopWindow()) RedrawAll();
1663 }
1664
GetParent()1665 nsIWidget* nsWindow::GetParent() { return mParent; }
1666
OnLoadRequest(nsIURI * aUri,int32_t aWindowType,int32_t aFlags,nsIPrincipal * aTriggeringPrincipal,bool aHasUserGesture,bool aIsTopLevel)1667 RefPtr<MozPromise<bool, bool, false>> nsWindow::OnLoadRequest(
1668 nsIURI* aUri, int32_t aWindowType, int32_t aFlags,
1669 nsIPrincipal* aTriggeringPrincipal, bool aHasUserGesture,
1670 bool aIsTopLevel) {
1671 if (!mGeckoViewSupport) {
1672 return MozPromise<bool, bool, false>::CreateAndResolve(false, __func__);
1673 }
1674 nsAutoCString spec, triggeringSpec;
1675 if (aUri) {
1676 aUri->GetDisplaySpec(spec);
1677 }
1678
1679 bool isNullPrincipal = false;
1680 if (aTriggeringPrincipal) {
1681 aTriggeringPrincipal->GetIsNullPrincipal(&isNullPrincipal);
1682
1683 if (!isNullPrincipal) {
1684 nsCOMPtr<nsIURI> triggeringUri;
1685 BasePrincipal::Cast(aTriggeringPrincipal)
1686 ->GetURI(getter_AddRefs(triggeringUri));
1687 if (triggeringUri) {
1688 triggeringUri->GetDisplaySpec(triggeringSpec);
1689 }
1690 }
1691 }
1692
1693 auto geckoResult = mGeckoViewSupport->OnLoadRequest(
1694 spec.get(), aWindowType, aFlags,
1695 isNullPrincipal ? nullptr : triggeringSpec.get(), aHasUserGesture,
1696 aIsTopLevel);
1697 return geckoResult
1698 ? MozPromise<bool, bool, false>::FromGeckoResult(geckoResult)
1699 : nullptr;
1700 }
1701
GetDPI()1702 float nsWindow::GetDPI() {
1703 float dpi = 160.0f;
1704
1705 nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
1706 if (screen) {
1707 screen->GetDpi(&dpi);
1708 }
1709
1710 return dpi;
1711 }
1712
GetDefaultScaleInternal()1713 double nsWindow::GetDefaultScaleInternal() {
1714 double scale = 1.0f;
1715
1716 nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
1717 if (screen) {
1718 screen->GetContentsScaleFactor(&scale);
1719 }
1720
1721 return scale;
1722 }
1723
Show(bool aState)1724 void nsWindow::Show(bool aState) {
1725 ALOG("nsWindow[%p]::Show %d", (void*)this, aState);
1726
1727 if (mWindowType == eWindowType_invisible) {
1728 ALOG("trying to show invisible window! ignoring..");
1729 return;
1730 }
1731
1732 if (aState == mIsVisible) return;
1733
1734 mIsVisible = aState;
1735
1736 if (IsTopLevel()) {
1737 // XXX should we bring this to the front when it's shown,
1738 // if it's a toplevel widget?
1739
1740 // XXX we should synthesize a eMouseExitFromWidget (for old top
1741 // window)/eMouseEnterIntoWidget (for new top window) since we need
1742 // to pretend that the top window always has focus. Not sure
1743 // if Show() is the right place to do this, though.
1744
1745 if (aState) {
1746 // It just became visible, so bring it to the front.
1747 BringToFront();
1748
1749 } else if (nsWindow::TopWindow() == this) {
1750 // find the next visible window to show
1751 unsigned int i;
1752 for (i = 1; i < gTopLevelWindows.Length(); i++) {
1753 nsWindow* win = gTopLevelWindows[i];
1754 if (!win->mIsVisible) continue;
1755
1756 win->BringToFront();
1757 break;
1758 }
1759 }
1760 } else if (FindTopLevel() == nsWindow::TopWindow()) {
1761 RedrawAll();
1762 }
1763
1764 #ifdef DEBUG_ANDROID_WIDGET
1765 DumpWindows();
1766 #endif
1767 }
1768
IsVisible() const1769 bool nsWindow::IsVisible() const { return mIsVisible; }
1770
ConstrainPosition(bool aAllowSlop,int32_t * aX,int32_t * aY)1771 void nsWindow::ConstrainPosition(bool aAllowSlop, int32_t* aX, int32_t* aY) {
1772 ALOG("nsWindow[%p]::ConstrainPosition %d [%d %d]", (void*)this, aAllowSlop,
1773 *aX, *aY);
1774
1775 // constrain toplevel windows; children we don't care about
1776 if (IsTopLevel()) {
1777 *aX = 0;
1778 *aY = 0;
1779 }
1780 }
1781
Move(double aX,double aY)1782 void nsWindow::Move(double aX, double aY) {
1783 if (IsTopLevel()) return;
1784
1785 Resize(aX, aY, mBounds.width, mBounds.height, true);
1786 }
1787
Resize(double aWidth,double aHeight,bool aRepaint)1788 void nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) {
1789 Resize(mBounds.x, mBounds.y, aWidth, aHeight, aRepaint);
1790 }
1791
Resize(double aX,double aY,double aWidth,double aHeight,bool aRepaint)1792 void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
1793 bool aRepaint) {
1794 ALOG("nsWindow[%p]::Resize [%f %f %f %f] (repaint %d)", (void*)this, aX, aY,
1795 aWidth, aHeight, aRepaint);
1796
1797 bool needPositionDispatch = aX != mBounds.x || aY != mBounds.y;
1798 bool needSizeDispatch = aWidth != mBounds.width || aHeight != mBounds.height;
1799
1800 mBounds.x = NSToIntRound(aX);
1801 mBounds.y = NSToIntRound(aY);
1802 mBounds.width = NSToIntRound(aWidth);
1803 mBounds.height = NSToIntRound(aHeight);
1804
1805 if (needSizeDispatch) {
1806 OnSizeChanged(gfx::IntSize::Truncate(aWidth, aHeight));
1807 }
1808
1809 if (needPositionDispatch) {
1810 NotifyWindowMoved(mBounds.x, mBounds.y);
1811 }
1812
1813 // Should we skip honoring aRepaint here?
1814 if (aRepaint && FindTopLevel() == nsWindow::TopWindow()) RedrawAll();
1815 }
1816
SetZIndex(int32_t aZIndex)1817 void nsWindow::SetZIndex(int32_t aZIndex) {
1818 ALOG("nsWindow[%p]::SetZIndex %d ignored", (void*)this, aZIndex);
1819 }
1820
SetSizeMode(nsSizeMode aMode)1821 void nsWindow::SetSizeMode(nsSizeMode aMode) {
1822 if (aMode == mSizeMode) {
1823 return;
1824 }
1825
1826 nsBaseWidget::SetSizeMode(aMode);
1827
1828 switch (aMode) {
1829 case nsSizeMode_Minimized:
1830 java::GeckoAppShell::MoveTaskToBack();
1831 break;
1832 case nsSizeMode_Fullscreen:
1833 MakeFullScreen(true);
1834 break;
1835 default:
1836 break;
1837 }
1838 }
1839
Enable(bool aState)1840 void nsWindow::Enable(bool aState) {
1841 ALOG("nsWindow[%p]::Enable %d ignored", (void*)this, aState);
1842 }
1843
IsEnabled() const1844 bool nsWindow::IsEnabled() const { return true; }
1845
Invalidate(const LayoutDeviceIntRect & aRect)1846 void nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) {}
1847
FindTopLevel()1848 nsWindow* nsWindow::FindTopLevel() {
1849 nsWindow* toplevel = this;
1850 while (toplevel) {
1851 if (toplevel->IsTopLevel()) return toplevel;
1852
1853 toplevel = toplevel->mParent;
1854 }
1855
1856 ALOG(
1857 "nsWindow::FindTopLevel(): couldn't find a toplevel or dialog window in "
1858 "this [%p] widget's hierarchy!",
1859 (void*)this);
1860 return this;
1861 }
1862
SetFocus(Raise,mozilla::dom::CallerType aCallerType)1863 void nsWindow::SetFocus(Raise, mozilla::dom::CallerType aCallerType) {
1864 FindTopLevel()->BringToFront();
1865 }
1866
BringToFront()1867 void nsWindow::BringToFront() {
1868 MOZ_ASSERT(XRE_IsParentProcess());
1869 // If the window to be raised is the same as the currently raised one,
1870 // do nothing. We need to check the focus manager as well, as the first
1871 // window that is created will be first in the window list but won't yet
1872 // be focused.
1873 nsFocusManager* fm = nsFocusManager::GetFocusManager();
1874 if (fm && fm->GetActiveWindow() && FindTopLevel() == nsWindow::TopWindow()) {
1875 return;
1876 }
1877
1878 if (!IsTopLevel()) {
1879 FindTopLevel()->BringToFront();
1880 return;
1881 }
1882
1883 RefPtr<nsWindow> kungFuDeathGrip(this);
1884
1885 nsWindow* oldTop = nullptr;
1886 if (!gTopLevelWindows.IsEmpty()) {
1887 oldTop = gTopLevelWindows[0];
1888 }
1889
1890 gTopLevelWindows.RemoveElement(this);
1891 gTopLevelWindows.InsertElementAt(0, this);
1892
1893 if (oldTop) {
1894 nsIWidgetListener* listener = oldTop->GetWidgetListener();
1895 if (listener) {
1896 listener->WindowDeactivated();
1897 }
1898 }
1899
1900 if (mWidgetListener) {
1901 mWidgetListener->WindowActivated();
1902 }
1903
1904 RedrawAll();
1905 }
1906
GetScreenBounds()1907 LayoutDeviceIntRect nsWindow::GetScreenBounds() {
1908 return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size());
1909 }
1910
WidgetToScreenOffset()1911 LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() {
1912 LayoutDeviceIntPoint p(0, 0);
1913
1914 for (nsWindow* w = this; !!w; w = w->mParent) {
1915 p.x += w->mBounds.x;
1916 p.y += w->mBounds.y;
1917
1918 if (w->IsTopLevel()) {
1919 break;
1920 }
1921 }
1922 return p;
1923 }
1924
DispatchEvent(WidgetGUIEvent * aEvent,nsEventStatus & aStatus)1925 nsresult nsWindow::DispatchEvent(WidgetGUIEvent* aEvent,
1926 nsEventStatus& aStatus) {
1927 aStatus = DispatchEvent(aEvent);
1928 return NS_OK;
1929 }
1930
DispatchEvent(WidgetGUIEvent * aEvent)1931 nsEventStatus nsWindow::DispatchEvent(WidgetGUIEvent* aEvent) {
1932 if (mAttachedWidgetListener) {
1933 return mAttachedWidgetListener->HandleEvent(aEvent, mUseAttachedEvents);
1934 } else if (mWidgetListener) {
1935 return mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents);
1936 }
1937 return nsEventStatus_eIgnore;
1938 }
1939
MakeFullScreen(bool aFullScreen,nsIScreen *)1940 nsresult nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen*) {
1941 if (!mAndroidView) {
1942 return NS_ERROR_NOT_AVAILABLE;
1943 }
1944
1945 mIsFullScreen = aFullScreen;
1946 mAndroidView->mEventDispatcher->Dispatch(
1947 aFullScreen ? u"GeckoView:FullScreenEnter" : u"GeckoView:FullScreenExit");
1948
1949 nsIWidgetListener* listener = GetWidgetListener();
1950 if (listener) {
1951 mSizeMode = mIsFullScreen ? nsSizeMode_Fullscreen : nsSizeMode_Normal;
1952 listener->SizeModeChanged(mSizeMode);
1953 listener->FullscreenChanged(mIsFullScreen);
1954 }
1955 return NS_OK;
1956 }
1957
GetLayerManager(PLayerTransactionChild *,LayersBackend,LayerManagerPersistence)1958 mozilla::layers::LayerManager* nsWindow::GetLayerManager(
1959 PLayerTransactionChild*, LayersBackend, LayerManagerPersistence) {
1960 if (mLayerManager) {
1961 return mLayerManager;
1962 }
1963
1964 if (mIsDisablingWebRender) {
1965 CreateLayerManager();
1966 mIsDisablingWebRender = false;
1967 return mLayerManager;
1968 }
1969
1970 return nullptr;
1971 }
1972
CreateLayerManager()1973 void nsWindow::CreateLayerManager() {
1974 if (mLayerManager) {
1975 return;
1976 }
1977
1978 nsWindow* topLevelWindow = FindTopLevel();
1979 if (!topLevelWindow || topLevelWindow->mWindowType == eWindowType_invisible) {
1980 // don't create a layer manager for an invisible top-level window
1981 return;
1982 }
1983
1984 // Ensure that gfxPlatform is initialized first.
1985 gfxPlatform::GetPlatform();
1986
1987 if (ShouldUseOffMainThreadCompositing()) {
1988 LayoutDeviceIntRect rect = GetBounds();
1989 CreateCompositor(rect.Width(), rect.Height());
1990 if (mLayerManager) {
1991 return;
1992 }
1993
1994 // If we get here, then off main thread compositing failed to initialize.
1995 sFailedToCreateGLContext = true;
1996 }
1997
1998 if (!ComputeShouldAccelerate() || sFailedToCreateGLContext) {
1999 printf_stderr(" -- creating basic, not accelerated\n");
2000 mLayerManager = CreateBasicLayerManager();
2001 }
2002 }
2003
NotifyDisablingWebRender()2004 void nsWindow::NotifyDisablingWebRender() {
2005 mIsDisablingWebRender = true;
2006 RedrawAll();
2007 }
2008
OnSizeChanged(const gfx::IntSize & aSize)2009 void nsWindow::OnSizeChanged(const gfx::IntSize& aSize) {
2010 ALOG("nsWindow: %p OnSizeChanged [%d %d]", (void*)this, aSize.width,
2011 aSize.height);
2012
2013 mBounds.width = aSize.width;
2014 mBounds.height = aSize.height;
2015
2016 if (mWidgetListener) {
2017 mWidgetListener->WindowResized(this, aSize.width, aSize.height);
2018 }
2019
2020 if (mAttachedWidgetListener) {
2021 mAttachedWidgetListener->WindowResized(this, aSize.width, aSize.height);
2022 }
2023 }
2024
InitEvent(WidgetGUIEvent & event,LayoutDeviceIntPoint * aPoint)2025 void nsWindow::InitEvent(WidgetGUIEvent& event, LayoutDeviceIntPoint* aPoint) {
2026 if (aPoint) {
2027 event.mRefPoint = *aPoint;
2028 } else {
2029 event.mRefPoint = LayoutDeviceIntPoint(0, 0);
2030 }
2031
2032 event.mTime = PR_Now() / 1000;
2033 }
2034
UpdateOverscrollVelocity(const float aX,const float aY)2035 void nsWindow::UpdateOverscrollVelocity(const float aX, const float aY) {
2036 if (NativePtr<LayerViewSupport>::Locked lvs{mLayerViewSupport}) {
2037 const auto& compositor = lvs->GetJavaCompositor();
2038 if (AndroidBridge::IsJavaUiThread()) {
2039 compositor->UpdateOverscrollVelocity(aX, aY);
2040 return;
2041 }
2042
2043 DispatchToUiThread(
2044 "nsWindow::UpdateOverscrollVelocity",
2045 [compositor = GeckoSession::Compositor::GlobalRef(compositor), aX, aY] {
2046 compositor->UpdateOverscrollVelocity(aX, aY);
2047 });
2048 }
2049 }
2050
UpdateOverscrollOffset(const float aX,const float aY)2051 void nsWindow::UpdateOverscrollOffset(const float aX, const float aY) {
2052 if (NativePtr<LayerViewSupport>::Locked lvs{mLayerViewSupport}) {
2053 const auto& compositor = lvs->GetJavaCompositor();
2054 if (AndroidBridge::IsJavaUiThread()) {
2055 compositor->UpdateOverscrollOffset(aX, aY);
2056 return;
2057 }
2058
2059 DispatchToUiThread(
2060 "nsWindow::UpdateOverscrollOffset",
2061 [compositor = GeckoSession::Compositor::GlobalRef(compositor), aX, aY] {
2062 compositor->UpdateOverscrollOffset(aX, aY);
2063 });
2064 }
2065 }
2066
GetNativeData(uint32_t aDataType)2067 void* nsWindow::GetNativeData(uint32_t aDataType) {
2068 switch (aDataType) {
2069 // used by GLContextProviderEGL, nullptr is EGL_DEFAULT_DISPLAY
2070 case NS_NATIVE_DISPLAY:
2071 return nullptr;
2072
2073 case NS_NATIVE_WIDGET:
2074 return (void*)this;
2075
2076 case NS_RAW_NATIVE_IME_CONTEXT: {
2077 void* pseudoIMEContext = GetPseudoIMEContext();
2078 if (pseudoIMEContext) {
2079 return pseudoIMEContext;
2080 }
2081 // We assume that there is only one context per process on Android
2082 return NS_ONLY_ONE_NATIVE_IME_CONTEXT;
2083 }
2084
2085 case NS_JAVA_SURFACE:
2086 if (NativePtr<LayerViewSupport>::Locked lvs{mLayerViewSupport}) {
2087 return lvs->GetSurface().Get();
2088 }
2089 return nullptr;
2090 }
2091
2092 return nullptr;
2093 }
2094
SetNativeData(uint32_t aDataType,uintptr_t aVal)2095 void nsWindow::SetNativeData(uint32_t aDataType, uintptr_t aVal) {
2096 switch (aDataType) {}
2097 }
2098
DispatchHitTest(const WidgetTouchEvent & aEvent)2099 void nsWindow::DispatchHitTest(const WidgetTouchEvent& aEvent) {
2100 if (aEvent.mMessage == eTouchStart && aEvent.mTouches.Length() == 1) {
2101 // Since touch events don't get retargeted by PositionedEventTargeting.cpp
2102 // code, we dispatch a dummy mouse event that *does* get retargeted.
2103 // Front-end code can use this to activate the highlight element in case
2104 // this touchstart is the start of a tap.
2105 WidgetMouseEvent hittest(true, eMouseHitTest, this,
2106 WidgetMouseEvent::eReal);
2107 hittest.mRefPoint = aEvent.mTouches[0]->mRefPoint;
2108 hittest.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
2109 nsEventStatus status;
2110 DispatchEvent(&hittest, status);
2111 }
2112 }
2113
GetModifiers(int32_t metaState)2114 mozilla::Modifiers nsWindow::GetModifiers(int32_t metaState) {
2115 using mozilla::java::sdk::KeyEvent;
2116 return (metaState & KeyEvent::META_ALT_MASK ? MODIFIER_ALT : 0) |
2117 (metaState & KeyEvent::META_SHIFT_MASK ? MODIFIER_SHIFT : 0) |
2118 (metaState & KeyEvent::META_CTRL_MASK ? MODIFIER_CONTROL : 0) |
2119 (metaState & KeyEvent::META_META_MASK ? MODIFIER_META : 0) |
2120 (metaState & KeyEvent::META_FUNCTION_ON ? MODIFIER_FN : 0) |
2121 (metaState & KeyEvent::META_CAPS_LOCK_ON ? MODIFIER_CAPSLOCK : 0) |
2122 (metaState & KeyEvent::META_NUM_LOCK_ON ? MODIFIER_NUMLOCK : 0) |
2123 (metaState & KeyEvent::META_SCROLL_LOCK_ON ? MODIFIER_SCROLLLOCK : 0);
2124 }
2125
GetEventTimeStamp(int64_t aEventTime)2126 TimeStamp nsWindow::GetEventTimeStamp(int64_t aEventTime) {
2127 // Android's event time is SystemClock.uptimeMillis that is counted in ms
2128 // since OS was booted.
2129 // (https://developer.android.com/reference/android/os/SystemClock.html)
2130 // and this SystemClock.uptimeMillis uses SYSTEM_TIME_MONOTONIC.
2131 // Our posix implemententaion of TimeStamp::Now uses SYSTEM_TIME_MONOTONIC
2132 // too. Due to same implementation, we can use this via FromSystemTime.
2133 int64_t tick =
2134 BaseTimeDurationPlatformUtils::TicksFromMilliseconds(aEventTime);
2135 return TimeStamp::FromSystemTime(tick);
2136 }
2137
OnReady(jni::Object::Param aQueue)2138 void nsWindow::GeckoViewSupport::OnReady(jni::Object::Param aQueue) {
2139 GeckoSession::Window::LocalRef window(mGeckoViewWindow);
2140 if (!window) {
2141 return;
2142 }
2143 window->OnReady(aQueue);
2144 mIsReady = true;
2145 }
2146
UserActivity()2147 void nsWindow::UserActivity() {
2148 if (!mIdleService) {
2149 mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
2150 }
2151
2152 if (mIdleService) {
2153 mIdleService->ResetIdleTimeOut(0);
2154 }
2155
2156 if (FindTopLevel() != nsWindow::TopWindow()) {
2157 BringToFront();
2158 }
2159 }
2160
GetNativeTextEventDispatcherListener()2161 TextEventDispatcherListener* nsWindow::GetNativeTextEventDispatcherListener() {
2162 nsWindow* top = FindTopLevel();
2163 MOZ_ASSERT(top);
2164
2165 if (!top->mEditableSupport) {
2166 // Non-GeckoView windows don't support IME operations.
2167 return nullptr;
2168 }
2169 return top->mEditableSupport;
2170 }
2171
SetInputContext(const InputContext & aContext,const InputContextAction & aAction)2172 void nsWindow::SetInputContext(const InputContext& aContext,
2173 const InputContextAction& aAction) {
2174 nsWindow* top = FindTopLevel();
2175 MOZ_ASSERT(top);
2176
2177 if (!top->mEditableSupport) {
2178 // Non-GeckoView windows don't support IME operations.
2179 return;
2180 }
2181
2182 // We are using an IME event later to notify Java, and the IME event
2183 // will be processed by the top window. Therefore, to ensure the
2184 // IME event uses the correct mInputContext, we need to let the top
2185 // window process SetInputContext
2186 top->mEditableSupport->SetInputContext(aContext, aAction);
2187 }
2188
GetInputContext()2189 InputContext nsWindow::GetInputContext() {
2190 nsWindow* top = FindTopLevel();
2191 MOZ_ASSERT(top);
2192
2193 if (!top->mEditableSupport) {
2194 // Non-GeckoView windows don't support IME operations.
2195 return InputContext();
2196 }
2197
2198 // We let the top window process SetInputContext,
2199 // so we should let it process GetInputContext as well.
2200 return top->mEditableSupport->GetInputContext();
2201 }
2202
SynthesizeNativeTouchPoint(uint32_t aPointerId,TouchPointerState aPointerState,LayoutDeviceIntPoint aPoint,double aPointerPressure,uint32_t aPointerOrientation,nsIObserver * aObserver)2203 nsresult nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
2204 TouchPointerState aPointerState,
2205 LayoutDeviceIntPoint aPoint,
2206 double aPointerPressure,
2207 uint32_t aPointerOrientation,
2208 nsIObserver* aObserver) {
2209 mozilla::widget::AutoObserverNotifier notifier(aObserver, "touchpoint");
2210
2211 int eventType;
2212 switch (aPointerState) {
2213 case TOUCH_CONTACT:
2214 // This could be a ACTION_DOWN or ACTION_MOVE depending on the
2215 // existing state; it is mapped to the right thing in Java.
2216 eventType = java::sdk::MotionEvent::ACTION_POINTER_DOWN;
2217 break;
2218 case TOUCH_REMOVE:
2219 // This could be turned into a ACTION_UP in Java
2220 eventType = java::sdk::MotionEvent::ACTION_POINTER_UP;
2221 break;
2222 case TOUCH_CANCEL:
2223 eventType = java::sdk::MotionEvent::ACTION_CANCEL;
2224 break;
2225 case TOUCH_HOVER: // not supported for now
2226 default:
2227 return NS_ERROR_UNEXPECTED;
2228 }
2229
2230 MOZ_ASSERT(mNPZCSupport);
2231 const auto& npzc = mNPZCSupport->GetJavaNPZC();
2232 const auto& bounds = FindTopLevel()->mBounds;
2233 aPoint.x -= bounds.x;
2234 aPoint.y -= bounds.y;
2235
2236 DispatchToUiThread(
2237 "nsWindow::SynthesizeNativeTouchPoint",
2238 [npzc = java::PanZoomController::NativeProvider::GlobalRef(npzc),
2239 aPointerId, eventType, aPoint, aPointerPressure, aPointerOrientation] {
2240 npzc->SynthesizeNativeTouchPoint(aPointerId, eventType, aPoint.x,
2241 aPoint.y, aPointerPressure,
2242 aPointerOrientation);
2243 });
2244 return NS_OK;
2245 }
2246
SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,uint32_t aNativeMessage,uint32_t aModifierFlags,nsIObserver * aObserver)2247 nsresult nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
2248 uint32_t aNativeMessage,
2249 uint32_t aModifierFlags,
2250 nsIObserver* aObserver) {
2251 mozilla::widget::AutoObserverNotifier notifier(aObserver, "mouseevent");
2252
2253 MOZ_ASSERT(mNPZCSupport);
2254 const auto& npzc = mNPZCSupport->GetJavaNPZC();
2255 const auto& bounds = FindTopLevel()->mBounds;
2256 aPoint.x -= bounds.x;
2257 aPoint.y -= bounds.y;
2258
2259 DispatchToUiThread(
2260 "nsWindow::SynthesizeNativeMouseEvent",
2261 [npzc = java::PanZoomController::NativeProvider::GlobalRef(npzc),
2262 aNativeMessage, aPoint] {
2263 npzc->SynthesizeNativeMouseEvent(aNativeMessage, aPoint.x, aPoint.y);
2264 });
2265 return NS_OK;
2266 }
2267
SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,nsIObserver * aObserver)2268 nsresult nsWindow::SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,
2269 nsIObserver* aObserver) {
2270 mozilla::widget::AutoObserverNotifier notifier(aObserver, "mouseevent");
2271
2272 MOZ_ASSERT(mNPZCSupport);
2273 const auto& npzc = mNPZCSupport->GetJavaNPZC();
2274 const auto& bounds = FindTopLevel()->mBounds;
2275 aPoint.x -= bounds.x;
2276 aPoint.y -= bounds.y;
2277
2278 DispatchToUiThread(
2279 "nsWindow::SynthesizeNativeMouseMove",
2280 [npzc = java::PanZoomController::NativeProvider::GlobalRef(npzc),
2281 aPoint] {
2282 npzc->SynthesizeNativeMouseEvent(
2283 java::sdk::MotionEvent::ACTION_HOVER_MOVE, aPoint.x, aPoint.y);
2284 });
2285 return NS_OK;
2286 }
2287
WidgetPaintsBackground()2288 bool nsWindow::WidgetPaintsBackground() {
2289 return StaticPrefs::android_widget_paints_background();
2290 }
2291
NeedsPaint()2292 bool nsWindow::NeedsPaint() {
2293 if (!mLayerViewSupport || mLayerViewSupport->CompositorPaused() ||
2294 // FindTopLevel() != nsWindow::TopWindow() ||
2295 !GetLayerManager(nullptr)) {
2296 return false;
2297 }
2298 return nsIWidget::NeedsPaint();
2299 }
2300
ConfigureAPZControllerThread()2301 void nsWindow::ConfigureAPZControllerThread() {
2302 nsCOMPtr<nsISerialEventTarget> thread = mozilla::GetAndroidUiThread();
2303 APZThreadUtils::SetControllerThread(thread);
2304 }
2305
2306 already_AddRefed<GeckoContentController>
CreateRootContentController()2307 nsWindow::CreateRootContentController() {
2308 RefPtr<GeckoContentController> controller =
2309 new AndroidContentController(this, mAPZEventState, mAPZC);
2310 return controller.forget();
2311 }
2312
GetMaxTouchPoints() const2313 uint32_t nsWindow::GetMaxTouchPoints() const {
2314 return java::GeckoAppShell::GetMaxTouchPoints();
2315 }
2316
UpdateZoomConstraints(const uint32_t & aPresShellId,const ScrollableLayerGuid::ViewID & aViewId,const mozilla::Maybe<ZoomConstraints> & aConstraints)2317 void nsWindow::UpdateZoomConstraints(
2318 const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId,
2319 const mozilla::Maybe<ZoomConstraints>& aConstraints) {
2320 nsBaseWidget::UpdateZoomConstraints(aPresShellId, aViewId, aConstraints);
2321 }
2322
GetCompositorBridgeChild() const2323 CompositorBridgeChild* nsWindow::GetCompositorBridgeChild() const {
2324 return mCompositorSession ? mCompositorSession->GetCompositorBridgeChild()
2325 : nullptr;
2326 }
2327
GetWidgetScreen()2328 already_AddRefed<nsIScreen> nsWindow::GetWidgetScreen() {
2329 RefPtr<nsIScreen> screen =
2330 ScreenHelperAndroid::GetSingleton()->ScreenForId(mScreenId);
2331 return screen.forget();
2332 }
2333
SetContentDocumentDisplayed(bool aDisplayed)2334 void nsWindow::SetContentDocumentDisplayed(bool aDisplayed) {
2335 mContentDocumentDisplayed = aDisplayed;
2336 }
2337
IsContentDocumentDisplayed()2338 bool nsWindow::IsContentDocumentDisplayed() {
2339 return mContentDocumentDisplayed;
2340 }
2341
RecvToolbarAnimatorMessageFromCompositor(int32_t aMessage)2342 void nsWindow::RecvToolbarAnimatorMessageFromCompositor(int32_t aMessage) {
2343 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
2344 if (NativePtr<LayerViewSupport>::Locked lvs{mLayerViewSupport}) {
2345 lvs->RecvToolbarAnimatorMessage(aMessage);
2346 }
2347 }
2348
UpdateRootFrameMetrics(const ScreenPoint & aScrollOffset,const CSSToScreenScale & aZoom)2349 void nsWindow::UpdateRootFrameMetrics(const ScreenPoint& aScrollOffset,
2350 const CSSToScreenScale& aZoom) {
2351 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
2352 if (NativePtr<LayerViewSupport>::Locked lvs{mLayerViewSupport}) {
2353 const auto& compositor = lvs->GetJavaCompositor();
2354 mContentDocumentDisplayed = true;
2355 compositor->UpdateRootFrameMetrics(aScrollOffset.x, aScrollOffset.y,
2356 aZoom.scale);
2357 }
2358 }
2359
RecvScreenPixels(Shmem && aMem,const ScreenIntSize & aSize)2360 void nsWindow::RecvScreenPixels(Shmem&& aMem, const ScreenIntSize& aSize) {
2361 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
2362 if (NativePtr<LayerViewSupport>::Locked lvs{mLayerViewSupport}) {
2363 lvs->RecvScreenPixels(std::move(aMem), aSize);
2364 }
2365 }
2366
UpdateDynamicToolbarMaxHeight(ScreenIntCoord aHeight)2367 void nsWindow::UpdateDynamicToolbarMaxHeight(ScreenIntCoord aHeight) {
2368 if (mDynamicToolbarMaxHeight == aHeight) {
2369 return;
2370 }
2371
2372 mDynamicToolbarMaxHeight = aHeight;
2373
2374 if (mWidgetListener) {
2375 mWidgetListener->DynamicToolbarMaxHeightChanged(aHeight);
2376 }
2377
2378 if (mAttachedWidgetListener) {
2379 mAttachedWidgetListener->DynamicToolbarMaxHeightChanged(aHeight);
2380 }
2381 }
2382
UpdateDynamicToolbarOffset(ScreenIntCoord aOffset)2383 void nsWindow::UpdateDynamicToolbarOffset(ScreenIntCoord aOffset) {
2384 if (mWidgetListener) {
2385 mWidgetListener->DynamicToolbarOffsetChanged(aOffset);
2386 }
2387
2388 if (mAttachedWidgetListener) {
2389 mAttachedWidgetListener->DynamicToolbarOffsetChanged(aOffset);
2390 }
2391 }
2392
GetSafeAreaInsets() const2393 ScreenIntMargin nsWindow::GetSafeAreaInsets() const { return mSafeAreaInsets; }
2394
UpdateSafeAreaInsets(const ScreenIntMargin & aSafeAreaInsets)2395 void nsWindow::UpdateSafeAreaInsets(const ScreenIntMargin& aSafeAreaInsets) {
2396 mSafeAreaInsets = aSafeAreaInsets;
2397
2398 if (mWidgetListener) {
2399 mWidgetListener->SafeAreaInsetsChanged(aSafeAreaInsets);
2400 }
2401
2402 if (mAttachedWidgetListener) {
2403 mAttachedWidgetListener->SafeAreaInsetsChanged(aSafeAreaInsets);
2404 }
2405 }
2406
OnLoadRequest(mozilla::jni::String::Param aUri,int32_t aWindowType,int32_t aFlags,mozilla::jni::String::Param aTriggeringUri,bool aHasUserGesture,bool aIsTopLevel) const2407 auto nsWindow::GeckoViewSupport::OnLoadRequest(
2408 mozilla::jni::String::Param aUri, int32_t aWindowType, int32_t aFlags,
2409 mozilla::jni::String::Param aTriggeringUri, bool aHasUserGesture,
2410 bool aIsTopLevel) const -> java::GeckoResult::LocalRef {
2411 GeckoSession::Window::LocalRef window(mGeckoViewWindow);
2412 if (!window) {
2413 return nullptr;
2414 }
2415 return window->OnLoadRequest(aUri, aWindowType, aFlags, aTriggeringUri,
2416 aHasUserGesture, aIsTopLevel);
2417 }
CreateTopLevelWindow()2418 already_AddRefed<nsIWidget> nsIWidget::CreateTopLevelWindow() {
2419 nsCOMPtr<nsIWidget> window = new nsWindow();
2420 return window.forget();
2421 }
2422
CreateChildWindow()2423 already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() {
2424 nsCOMPtr<nsIWidget> window = new nsWindow();
2425 return window.forget();
2426 }
2427