Bug 1755316 - Add audio tests with simultaneous processes r=alwu
[gecko.git] / widget / android / nsWindow.cpp
blob668d93aa5fb48f94786d2718e8328a4253eaabe7
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/. */
7 #include <algorithm>
8 #include <atomic>
9 #include <android/log.h>
10 #include <android/native_window.h>
11 #include <android/native_window_jni.h>
12 #include <math.h>
13 #include <queue>
14 #include <type_traits>
15 #include <unistd.h>
17 #include "AndroidGraphics.h"
18 #include "AndroidBridge.h"
19 #include "AndroidBridgeUtilities.h"
20 #include "AndroidCompositorWidget.h"
21 #include "AndroidContentController.h"
22 #include "AndroidUiThread.h"
23 #include "AndroidView.h"
24 #include "gfxContext.h"
25 #include "GeckoEditableSupport.h"
26 #include "GeckoViewSupport.h"
27 #include "GLContext.h"
28 #include "GLContextProvider.h"
29 #include "JavaBuiltins.h"
30 #include "JavaExceptions.h"
31 #include "KeyEvent.h"
32 #include "Layers.h"
33 #include "MotionEvent.h"
34 #include "ScopedGLHelpers.h"
35 #include "ScreenHelperAndroid.h"
36 #include "TouchResampler.h"
37 #include "WidgetUtils.h"
38 #include "WindowRenderer.h"
40 #include "nsAppShell.h"
41 #include "nsContentUtils.h"
42 #include "nsFocusManager.h"
43 #include "nsGkAtoms.h"
44 #include "nsGfxCIID.h"
45 #include "nsLayoutUtils.h"
46 #include "nsNetUtil.h"
47 #include "nsPrintfCString.h"
48 #include "nsString.h"
49 #include "nsTArray.h"
50 #include "nsThreadUtils.h"
51 #include "nsUserIdleService.h"
52 #include "nsViewManager.h"
53 #include "nsWidgetsCID.h"
54 #include "nsWindow.h"
56 #include "nsIWidgetListener.h"
57 #include "nsIWindowWatcher.h"
58 #include "nsIAppWindow.h"
60 #include "mozilla/MiscEvents.h"
61 #include "mozilla/MouseEvents.h"
62 #include "mozilla/Preferences.h"
63 #include "mozilla/StaticPrefs_android.h"
64 #include "mozilla/StaticPrefs_ui.h"
65 #include "mozilla/TouchEvents.h"
66 #include "mozilla/WeakPtr.h"
67 #include "mozilla/WheelHandlingHelper.h" // for WheelDeltaAdjustmentStrategy
68 #include "mozilla/a11y/SessionAccessibility.h"
69 #include "mozilla/dom/BrowsingContext.h"
70 #include "mozilla/dom/CanonicalBrowsingContext.h"
71 #include "mozilla/dom/ContentChild.h"
72 #include "mozilla/dom/ContentParent.h"
73 #include "mozilla/dom/MouseEventBinding.h"
74 #include "mozilla/gfx/2D.h"
75 #include "mozilla/gfx/DataSurfaceHelpers.h"
76 #include "mozilla/gfx/Swizzle.h"
77 #include "mozilla/gfx/Types.h"
78 #include "mozilla/ipc/Shmem.h"
79 #include "mozilla/java/EventDispatcherWrappers.h"
80 #include "mozilla/java/GeckoAppShellWrappers.h"
81 #include "mozilla/java/GeckoEditableChildWrappers.h"
82 #include "mozilla/java/GeckoResultWrappers.h"
83 #include "mozilla/java/GeckoSessionNatives.h"
84 #include "mozilla/java/GeckoSystemStateListenerWrappers.h"
85 #include "mozilla/java/PanZoomControllerNatives.h"
86 #include "mozilla/java/SessionAccessibilityWrappers.h"
87 #include "mozilla/layers/APZEventState.h"
88 #include "mozilla/layers/APZInputBridge.h"
89 #include "mozilla/layers/APZThreadUtils.h"
90 #include "mozilla/layers/CompositorBridgeChild.h"
91 #include "mozilla/layers/CompositorOGL.h"
92 #include "mozilla/layers/CompositorSession.h"
93 #include "mozilla/layers/LayersTypes.h"
94 #include "mozilla/layers/UiCompositorControllerChild.h"
95 #include "mozilla/layers/IAPZCTreeManager.h"
96 #include "mozilla/ProfilerLabels.h"
97 #include "mozilla/widget/AndroidVsync.h"
99 using namespace mozilla;
100 using namespace mozilla::dom;
101 using namespace mozilla::layers;
102 using namespace mozilla::widget;
103 using namespace mozilla::ipc;
105 using mozilla::dom::ContentChild;
106 using mozilla::dom::ContentParent;
107 using mozilla::gfx::DataSourceSurface;
108 using mozilla::gfx::IntSize;
109 using mozilla::gfx::Matrix;
110 using mozilla::gfx::SurfaceFormat;
111 using mozilla::java::GeckoSession;
113 // All the toplevel windows that have been created; these are in
114 // stacking order, so the window at gTopLevelWindows[0] is the topmost
115 // one.
116 static nsTArray<nsWindow*> gTopLevelWindows;
118 static bool sFailedToCreateGLContext = false;
120 // Multitouch swipe thresholds in inches
121 static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4;
122 static const double SWIPE_MIN_DISTANCE_INCHES = 0.6;
124 static const double kTouchResampleVsyncAdjustMs = 5.0;
126 static const int32_t INPUT_RESULT_UNHANDLED =
127 java::PanZoomController::INPUT_RESULT_UNHANDLED;
128 static const int32_t INPUT_RESULT_HANDLED =
129 java::PanZoomController::INPUT_RESULT_HANDLED;
130 static const int32_t INPUT_RESULT_HANDLED_CONTENT =
131 java::PanZoomController::INPUT_RESULT_HANDLED_CONTENT;
132 static const int32_t INPUT_RESULT_IGNORED =
133 java::PanZoomController::INPUT_RESULT_IGNORED;
135 static const nsCString::size_type MAX_TOPLEVEL_DATA_URI_LEN = 2 * 1024 * 1024;
137 // Unique ID given to each widget, to identify it for the
138 // CompositorSurfaceManager.
139 static std::atomic<int32_t> sWidgetId{0};
141 namespace {
142 template <class Instance, class Impl>
143 std::enable_if_t<jni::detail::NativePtrPicker<Impl>::value ==
144 jni::detail::NativePtrType::REFPTR,
145 void>
146 CallAttachNative(Instance aInstance, Impl* aImpl) {
147 Impl::AttachNative(aInstance, RefPtr<Impl>(aImpl).get());
150 template <class Instance, class Impl>
151 std::enable_if_t<jni::detail::NativePtrPicker<Impl>::value ==
152 jni::detail::NativePtrType::OWNING,
153 void>
154 CallAttachNative(Instance aInstance, Impl* aImpl) {
155 Impl::AttachNative(aInstance, UniquePtr<Impl>(aImpl));
158 template <class Lambda>
159 bool DispatchToUiThread(const char* aName, Lambda&& aLambda) {
160 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
161 uiThread->Dispatch(NS_NewRunnableFunction(aName, std::move(aLambda)));
162 return true;
164 return false;
166 } // namespace
168 namespace mozilla {
169 namespace widget {
171 using WindowPtr = jni::NativeWeakPtr<GeckoViewSupport>;
174 * PanZoomController handles its native calls on the UI thread, so make
175 * it separate from GeckoViewSupport.
177 class NPZCSupport final
178 : public java::PanZoomController::NativeProvider::Natives<NPZCSupport>,
179 public AndroidVsync::Observer {
180 WindowPtr mWindow;
181 java::PanZoomController::NativeProvider::WeakRef mNPZC;
183 // Stores the returnResult of each pending motion event between
184 // HandleMotionEvent and FinishHandlingMotionEvent.
185 std::queue<std::pair<uint64_t, java::GeckoResult::GlobalRef>>
186 mPendingMotionEventReturnResults;
188 RefPtr<AndroidVsync> mAndroidVsync;
189 TouchResampler mTouchResampler;
190 int mPreviousButtons = 0;
191 bool mListeningToVsync = false;
193 // Only true if mAndroidVsync is non-null and the resampling pref is set.
194 bool mTouchResamplingEnabled = false;
196 template <typename Lambda>
197 class InputEvent final : public nsAppShell::Event {
198 java::PanZoomController::NativeProvider::GlobalRef mNPZC;
199 Lambda mLambda;
201 public:
202 InputEvent(const NPZCSupport* aNPZCSupport, Lambda&& aLambda)
203 : mNPZC(aNPZCSupport->mNPZC), mLambda(std::move(aLambda)) {}
205 void Run() override {
206 MOZ_ASSERT(NS_IsMainThread());
208 JNIEnv* const env = jni::GetGeckoThreadEnv();
209 const auto npzcSupportWeak = GetNative(
210 java::PanZoomController::NativeProvider::LocalRef(env, mNPZC));
211 if (!npzcSupportWeak) {
212 // We already shut down.
213 env->ExceptionClear();
214 return;
217 auto acc = npzcSupportWeak->Access();
218 if (!acc) {
219 // We already shut down.
220 env->ExceptionClear();
221 return;
224 auto win = acc->mWindow.Access();
225 if (!win) {
226 // We already shut down.
227 env->ExceptionClear();
228 return;
231 nsWindow* const window = win->GetNsWindow();
232 if (!window) {
233 // We already shut down.
234 env->ExceptionClear();
235 return;
238 window->UserActivity();
239 return mLambda(window);
242 bool IsUIEvent() const override { return true; }
245 template <typename Lambda>
246 void PostInputEvent(Lambda&& aLambda) {
247 // Use priority queue for input events.
248 nsAppShell::PostEvent(
249 MakeUnique<InputEvent<Lambda>>(this, std::move(aLambda)));
252 public:
253 typedef java::PanZoomController::NativeProvider::Natives<NPZCSupport> Base;
255 NPZCSupport(WindowPtr aWindow,
256 const java::PanZoomController::NativeProvider::LocalRef& aNPZC)
257 : mWindow(aWindow), mNPZC(aNPZC) {
258 #if defined(DEBUG)
259 auto win(mWindow.Access());
260 MOZ_ASSERT(!!win);
261 #endif // defined(DEBUG)
263 // Use vsync for touch resampling on API level 19 and above.
264 // See gfxAndroidPlatform::CreateHardwareVsyncSource() for comparison.
265 if (jni::GetAPIVersion() >= 19) {
266 mAndroidVsync = AndroidVsync::GetInstance();
270 ~NPZCSupport() {
271 if (mListeningToVsync) {
272 MOZ_RELEASE_ASSERT(mAndroidVsync);
273 mAndroidVsync->UnregisterObserver(this, AndroidVsync::INPUT);
274 mListeningToVsync = false;
278 using Base::AttachNative;
279 using Base::DisposeNative;
281 void OnWeakNonIntrusiveDetach(already_AddRefed<Runnable> aDisposer) {
282 RefPtr<Runnable> disposer = aDisposer;
283 // There are several considerations when shutting down NPZC. 1) The
284 // Gecko thread may destroy NPZC at any time when nsWindow closes. 2)
285 // There may be pending events on the Gecko thread when NPZC is
286 // destroyed. 3) mWindow may not be available when the pending event
287 // runs. 4) The UI thread may destroy NPZC at any time when GeckoView
288 // is destroyed. 5) The UI thread may destroy NPZC at the same time as
289 // Gecko thread trying to destroy NPZC. 6) There may be pending calls
290 // on the UI thread when NPZC is destroyed. 7) mWindow may have been
291 // cleared on the Gecko thread when the pending call happens on the UI
292 // thread.
294 // 1) happens through OnWeakNonIntrusiveDetach, which first notifies the UI
295 // thread through Destroy; Destroy then calls DisposeNative, which
296 // finally disposes the native instance back on the Gecko thread. Using
297 // Destroy to indirectly call DisposeNative here also solves 5), by
298 // making everything go through the UI thread, avoiding contention.
300 // 2) and 3) are solved by clearing mWindow, which signals to the
301 // pending event that we had shut down. In that case the event bails
302 // and does not touch mWindow.
304 // 4) happens through DisposeNative directly.
306 // 6) is solved by keeping a destroyed flag in the Java NPZC instance,
307 // and only make a pending call if the destroyed flag is not set.
309 // 7) is solved by taking a lock whenever mWindow is modified on the
310 // Gecko thread or accessed on the UI thread. That way, we don't
311 // release mWindow until the UI thread is done using it, thus avoiding
312 // the race condition.
314 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
315 auto npzc = java::PanZoomController::NativeProvider::GlobalRef(mNPZC);
316 if (!npzc) {
317 return;
320 uiThread->Dispatch(
321 NS_NewRunnableFunction("NPZCSupport::OnWeakNonIntrusiveDetach",
322 [npzc, disposer = std::move(disposer)] {
323 npzc->SetAttached(false);
324 disposer->Run();
325 }));
329 const java::PanZoomController::NativeProvider::Ref& GetJavaNPZC() const {
330 return mNPZC;
333 public:
334 void SetIsLongpressEnabled(bool aIsLongpressEnabled) {
335 RefPtr<IAPZCTreeManager> controller;
337 if (auto window = mWindow.Access()) {
338 nsWindow* gkWindow = window->GetNsWindow();
339 if (gkWindow) {
340 controller = gkWindow->mAPZC;
344 if (controller) {
345 controller->SetLongTapEnabled(aIsLongpressEnabled);
349 int32_t HandleScrollEvent(int64_t aTime, int32_t aMetaState, float aX,
350 float aY, float aHScroll, float aVScroll) {
351 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
353 RefPtr<IAPZCTreeManager> controller;
355 if (auto window = mWindow.Access()) {
356 nsWindow* gkWindow = window->GetNsWindow();
357 if (gkWindow) {
358 controller = gkWindow->mAPZC;
362 if (!controller) {
363 return INPUT_RESULT_UNHANDLED;
366 ScreenPoint origin = ScreenPoint(aX, aY);
368 if (StaticPrefs::ui_scrolling_negate_wheel_scroll()) {
369 aHScroll = -aHScroll;
370 aVScroll = -aVScroll;
373 ScrollWheelInput input(
374 aTime, nsWindow::GetEventTimeStamp(aTime),
375 nsWindow::GetModifiers(aMetaState), ScrollWheelInput::SCROLLMODE_SMOOTH,
376 ScrollWheelInput::SCROLLDELTA_PIXEL, origin, aHScroll, aVScroll, false,
377 // XXX Do we need to support auto-dir scrolling
378 // for Android widgets with a wheel device?
379 // Currently, I just leave it unimplemented. If
380 // we need to implement it, what's the extra work
381 // to do?
382 WheelDeltaAdjustmentStrategy::eNone);
384 APZEventResult result = controller->InputBridge()->ReceiveInputEvent(input);
385 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
386 return INPUT_RESULT_IGNORED;
389 PostInputEvent([input, result](nsWindow* window) {
390 WidgetWheelEvent wheelEvent = input.ToWidgetEvent(window);
391 window->ProcessUntransformedAPZEvent(&wheelEvent, result);
394 switch (result.GetStatus()) {
395 case nsEventStatus_eIgnore:
396 return INPUT_RESULT_UNHANDLED;
397 case nsEventStatus_eConsumeDoDefault:
398 return result.GetHandledResult()->IsHandledByRoot()
399 ? INPUT_RESULT_HANDLED
400 : INPUT_RESULT_HANDLED_CONTENT;
401 default:
402 MOZ_ASSERT_UNREACHABLE("Unexpected nsEventStatus");
403 return INPUT_RESULT_UNHANDLED;
407 private:
408 static MouseInput::ButtonType GetButtonType(int button) {
409 MouseInput::ButtonType result = MouseInput::NONE;
411 switch (button) {
412 case java::sdk::MotionEvent::BUTTON_PRIMARY:
413 result = MouseInput::PRIMARY_BUTTON;
414 break;
415 case java::sdk::MotionEvent::BUTTON_SECONDARY:
416 result = MouseInput::SECONDARY_BUTTON;
417 break;
418 case java::sdk::MotionEvent::BUTTON_TERTIARY:
419 result = MouseInput::MIDDLE_BUTTON;
420 break;
421 default:
422 break;
425 return result;
428 static int16_t ConvertButtons(int buttons) {
429 int16_t result = 0;
431 if (buttons & java::sdk::MotionEvent::BUTTON_PRIMARY) {
432 result |= MouseButtonsFlag::ePrimaryFlag;
434 if (buttons & java::sdk::MotionEvent::BUTTON_SECONDARY) {
435 result |= MouseButtonsFlag::eSecondaryFlag;
437 if (buttons & java::sdk::MotionEvent::BUTTON_TERTIARY) {
438 result |= MouseButtonsFlag::eMiddleFlag;
440 if (buttons & java::sdk::MotionEvent::BUTTON_BACK) {
441 result |= MouseButtonsFlag::e4thFlag;
443 if (buttons & java::sdk::MotionEvent::BUTTON_FORWARD) {
444 result |= MouseButtonsFlag::e5thFlag;
447 return result;
450 static int32_t ConvertAPZHandledPlace(APZHandledPlace aHandledPlace) {
451 switch (aHandledPlace) {
452 case APZHandledPlace::Unhandled:
453 return INPUT_RESULT_UNHANDLED;
454 case APZHandledPlace::HandledByRoot:
455 return INPUT_RESULT_HANDLED;
456 case APZHandledPlace::HandledByContent:
457 return INPUT_RESULT_HANDLED_CONTENT;
458 case APZHandledPlace::Invalid:
459 MOZ_ASSERT_UNREACHABLE("The handled result should NOT be Invalid");
460 return INPUT_RESULT_UNHANDLED;
462 MOZ_ASSERT_UNREACHABLE("Unknown handled result");
463 return INPUT_RESULT_UNHANDLED;
466 static int32_t ConvertSideBits(SideBits aSideBits) {
467 int32_t ret = java::PanZoomController::SCROLLABLE_FLAG_NONE;
468 if (aSideBits & SideBits::eTop) {
469 ret |= java::PanZoomController::SCROLLABLE_FLAG_TOP;
471 if (aSideBits & SideBits::eRight) {
472 ret |= java::PanZoomController::SCROLLABLE_FLAG_RIGHT;
474 if (aSideBits & SideBits::eBottom) {
475 ret |= java::PanZoomController::SCROLLABLE_FLAG_BOTTOM;
477 if (aSideBits & SideBits::eLeft) {
478 ret |= java::PanZoomController::SCROLLABLE_FLAG_LEFT;
480 return ret;
483 static int32_t ConvertScrollDirections(
484 layers::ScrollDirections aScrollDirections) {
485 int32_t ret = java::PanZoomController::OVERSCROLL_FLAG_NONE;
486 if (aScrollDirections.contains(layers::HorizontalScrollDirection)) {
487 ret |= java::PanZoomController::OVERSCROLL_FLAG_HORIZONTAL;
489 if (aScrollDirections.contains(layers::VerticalScrollDirection)) {
490 ret |= java::PanZoomController::OVERSCROLL_FLAG_VERTICAL;
492 return ret;
495 static java::PanZoomController::InputResultDetail::LocalRef
496 ConvertAPZHandledResult(const APZHandledResult& aHandledResult) {
497 return java::PanZoomController::InputResultDetail::New(
498 ConvertAPZHandledPlace(aHandledResult.mPlace),
499 ConvertSideBits(aHandledResult.mScrollableDirections),
500 ConvertScrollDirections(aHandledResult.mOverscrollDirections));
503 public:
504 int32_t HandleMouseEvent(int32_t aAction, int64_t aTime, int32_t aMetaState,
505 float aX, float aY, int buttons) {
506 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
508 RefPtr<IAPZCTreeManager> controller;
510 if (auto window = mWindow.Access()) {
511 nsWindow* gkWindow = window->GetNsWindow();
512 if (gkWindow) {
513 controller = gkWindow->mAPZC;
517 if (!controller) {
518 return INPUT_RESULT_UNHANDLED;
521 MouseInput::MouseType mouseType = MouseInput::MOUSE_NONE;
522 MouseInput::ButtonType buttonType = MouseInput::NONE;
523 switch (aAction) {
524 case java::sdk::MotionEvent::ACTION_DOWN:
525 mouseType = MouseInput::MOUSE_DOWN;
526 buttonType = GetButtonType(buttons ^ mPreviousButtons);
527 mPreviousButtons = buttons;
528 break;
529 case java::sdk::MotionEvent::ACTION_UP:
530 mouseType = MouseInput::MOUSE_UP;
531 buttonType = GetButtonType(buttons ^ mPreviousButtons);
532 mPreviousButtons = buttons;
533 break;
534 case java::sdk::MotionEvent::ACTION_MOVE:
535 mouseType = MouseInput::MOUSE_MOVE;
536 break;
537 case java::sdk::MotionEvent::ACTION_HOVER_MOVE:
538 mouseType = MouseInput::MOUSE_MOVE;
539 break;
540 case java::sdk::MotionEvent::ACTION_HOVER_ENTER:
541 mouseType = MouseInput::MOUSE_WIDGET_ENTER;
542 break;
543 case java::sdk::MotionEvent::ACTION_HOVER_EXIT:
544 mouseType = MouseInput::MOUSE_WIDGET_EXIT;
545 break;
546 default:
547 break;
550 if (mouseType == MouseInput::MOUSE_NONE) {
551 return INPUT_RESULT_UNHANDLED;
554 ScreenPoint origin = ScreenPoint(aX, aY);
556 MouseInput input(
557 mouseType, buttonType, MouseEvent_Binding::MOZ_SOURCE_MOUSE,
558 ConvertButtons(buttons), origin, aTime,
559 nsWindow::GetEventTimeStamp(aTime), nsWindow::GetModifiers(aMetaState));
561 APZEventResult result = controller->InputBridge()->ReceiveInputEvent(input);
562 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
563 return INPUT_RESULT_IGNORED;
566 PostInputEvent([input, result](nsWindow* window) {
567 WidgetMouseEvent mouseEvent = input.ToWidgetEvent(window);
568 window->ProcessUntransformedAPZEvent(&mouseEvent, result);
571 switch (result.GetStatus()) {
572 case nsEventStatus_eIgnore:
573 return INPUT_RESULT_UNHANDLED;
574 case nsEventStatus_eConsumeDoDefault:
575 return result.GetHandledResult()->IsHandledByRoot()
576 ? INPUT_RESULT_HANDLED
577 : INPUT_RESULT_HANDLED_CONTENT;
578 default:
579 MOZ_ASSERT_UNREACHABLE("Unexpected nsEventStatus");
580 return INPUT_RESULT_UNHANDLED;
584 // Convert MotionEvent touch radius and orientation into the format required
585 // by w3c touchevents.
586 // toolMajor and toolMinor span a rectangle that's oriented as per
587 // aOrientation, centered around the touch point.
588 static std::pair<float, ScreenSize> ConvertOrientationAndRadius(
589 float aOrientation, float aToolMajor, float aToolMinor) {
590 float angle = aOrientation * 180.0f / M_PI;
591 // w3c touchevents spec does not allow orientations == 90
592 // this shifts it to -90, which will be shifted to zero below
593 if (angle >= 90.0) {
594 angle -= 180.0f;
597 // w3c touchevent radii are given with an orientation between 0 and
598 // 90. The radii are found by removing the orientation and
599 // measuring the x and y radii of the resulting ellipse. For
600 // Android orientations >= 0 and < 90, use the y radius as the
601 // major radius, and x as the minor radius. However, for an
602 // orientation < 0, we have to shift the orientation by adding 90,
603 // and reverse which radius is major and minor.
604 ScreenSize radius;
605 if (angle < 0.0f) {
606 angle += 90.0f;
607 radius =
608 ScreenSize(int32_t(aToolMajor / 2.0f), int32_t(aToolMinor / 2.0f));
609 } else {
610 radius =
611 ScreenSize(int32_t(aToolMinor / 2.0f), int32_t(aToolMajor / 2.0f));
614 return std::make_pair(angle, radius);
617 void HandleMotionEvent(
618 const java::PanZoomController::NativeProvider::LocalRef& aInstance,
619 jni::Object::Param aEventData, float aScreenX, float aScreenY,
620 jni::Object::Param aResult) {
621 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
623 auto returnResult = java::GeckoResult::Ref::From(aResult);
624 auto eventData =
625 java::PanZoomController::MotionEventData::Ref::From(aEventData);
626 nsTArray<int32_t> pointerId(eventData->PointerId()->GetElements());
627 size_t pointerCount = pointerId.Length();
628 MultiTouchInput::MultiTouchType type;
629 size_t startIndex = 0;
630 size_t endIndex = pointerCount;
632 switch (eventData->Action()) {
633 case java::sdk::MotionEvent::ACTION_DOWN:
634 case java::sdk::MotionEvent::ACTION_POINTER_DOWN:
635 type = MultiTouchInput::MULTITOUCH_START;
636 break;
637 case java::sdk::MotionEvent::ACTION_MOVE:
638 type = MultiTouchInput::MULTITOUCH_MOVE;
639 break;
640 case java::sdk::MotionEvent::ACTION_UP:
641 case java::sdk::MotionEvent::ACTION_POINTER_UP:
642 // for pointer-up events we only want the data from
643 // the one pointer that went up
644 type = MultiTouchInput::MULTITOUCH_END;
645 startIndex = eventData->ActionIndex();
646 endIndex = startIndex + 1;
647 break;
648 case java::sdk::MotionEvent::ACTION_OUTSIDE:
649 case java::sdk::MotionEvent::ACTION_CANCEL:
650 type = MultiTouchInput::MULTITOUCH_CANCEL;
651 break;
652 default:
653 if (returnResult) {
654 returnResult->Complete(
655 java::sdk::Integer::ValueOf(INPUT_RESULT_UNHANDLED));
657 return;
660 MultiTouchInput input(type, eventData->Time(),
661 nsWindow::GetEventTimeStamp(eventData->Time()), 0);
662 input.modifiers = nsWindow::GetModifiers(eventData->MetaState());
663 input.mTouches.SetCapacity(endIndex - startIndex);
664 input.mScreenOffset =
665 ExternalIntPoint(int32_t(floorf(aScreenX)), int32_t(floorf(aScreenY)));
667 size_t historySize = eventData->HistorySize();
668 nsTArray<int64_t> historicalTime(
669 eventData->HistoricalTime()->GetElements());
670 MOZ_RELEASE_ASSERT(historicalTime.Length() == historySize);
672 // Each of these is |historySize| sets of |pointerCount| values.
673 size_t historicalDataCount = historySize * pointerCount;
674 nsTArray<float> historicalX(eventData->HistoricalX()->GetElements());
675 nsTArray<float> historicalY(eventData->HistoricalY()->GetElements());
676 nsTArray<float> historicalOrientation(
677 eventData->HistoricalOrientation()->GetElements());
678 nsTArray<float> historicalPressure(
679 eventData->HistoricalPressure()->GetElements());
680 nsTArray<float> historicalToolMajor(
681 eventData->HistoricalToolMajor()->GetElements());
682 nsTArray<float> historicalToolMinor(
683 eventData->HistoricalToolMinor()->GetElements());
685 MOZ_RELEASE_ASSERT(historicalX.Length() == historicalDataCount);
686 MOZ_RELEASE_ASSERT(historicalY.Length() == historicalDataCount);
687 MOZ_RELEASE_ASSERT(historicalOrientation.Length() == historicalDataCount);
688 MOZ_RELEASE_ASSERT(historicalPressure.Length() == historicalDataCount);
689 MOZ_RELEASE_ASSERT(historicalToolMajor.Length() == historicalDataCount);
690 MOZ_RELEASE_ASSERT(historicalToolMinor.Length() == historicalDataCount);
692 // Each of these is |pointerCount| values.
693 nsTArray<float> x(eventData->X()->GetElements());
694 nsTArray<float> y(eventData->Y()->GetElements());
695 nsTArray<float> orientation(eventData->Orientation()->GetElements());
696 nsTArray<float> pressure(eventData->Pressure()->GetElements());
697 nsTArray<float> toolMajor(eventData->ToolMajor()->GetElements());
698 nsTArray<float> toolMinor(eventData->ToolMinor()->GetElements());
700 MOZ_ASSERT(x.Length() == pointerCount);
701 MOZ_ASSERT(y.Length() == pointerCount);
702 MOZ_ASSERT(orientation.Length() == pointerCount);
703 MOZ_ASSERT(pressure.Length() == pointerCount);
704 MOZ_ASSERT(toolMajor.Length() == pointerCount);
705 MOZ_ASSERT(toolMinor.Length() == pointerCount);
707 for (size_t i = startIndex; i < endIndex; i++) {
708 auto [orien, radius] = ConvertOrientationAndRadius(
709 orientation[i], toolMajor[i], toolMinor[i]);
711 ScreenIntPoint point(int32_t(floorf(x[i])), int32_t(floorf(y[i])));
712 SingleTouchData singleTouchData(pointerId[i], point, radius, orien,
713 pressure[i]);
715 for (size_t historyIndex = 0; historyIndex < historySize;
716 historyIndex++) {
717 size_t historicalI = historyIndex * pointerCount + i;
718 auto [historicalAngle, historicalRadius] = ConvertOrientationAndRadius(
719 historicalOrientation[historicalI],
720 historicalToolMajor[historicalI], historicalToolMinor[historicalI]);
721 ScreenIntPoint historicalPoint(
722 int32_t(floorf(historicalX[historicalI])),
723 int32_t(floorf(historicalY[historicalI])));
724 singleTouchData.mHistoricalData.AppendElement(
725 SingleTouchData::HistoricalTouchData{
726 nsWindow::GetEventTimeStamp(historicalTime[historyIndex]),
727 historicalPoint,
728 {}, // mLocalScreenPoint will be computed later by APZ
729 historicalRadius,
730 historicalAngle,
731 historicalPressure[historicalI]});
734 input.mTouches.AppendElement(singleTouchData);
737 if (mAndroidVsync &&
738 eventData->Action() == java::sdk::MotionEvent::ACTION_DOWN) {
739 // Query pref value at the beginning of a touch gesture so that we don't
740 // leave events stuck in the resampler after a pref flip.
741 mTouchResamplingEnabled = StaticPrefs::android_touch_resampling_enabled();
744 if (!mTouchResamplingEnabled) {
745 FinishHandlingMotionEvent(std::move(input),
746 java::GeckoResult::LocalRef(returnResult));
747 return;
750 uint64_t eventId = mTouchResampler.ProcessEvent(std::move(input));
751 mPendingMotionEventReturnResults.push(
752 {eventId, java::GeckoResult::GlobalRef(returnResult)});
754 RegisterOrUnregisterForVsync(mTouchResampler.InTouchingState());
755 ConsumeMotionEventsFromResampler();
758 void RegisterOrUnregisterForVsync(bool aNeedVsync) {
759 MOZ_RELEASE_ASSERT(mAndroidVsync);
760 if (aNeedVsync && !mListeningToVsync) {
761 mAndroidVsync->RegisterObserver(this, AndroidVsync::INPUT);
762 } else if (!aNeedVsync && mListeningToVsync) {
763 mAndroidVsync->UnregisterObserver(this, AndroidVsync::INPUT);
765 mListeningToVsync = aNeedVsync;
768 void OnVsync(const TimeStamp& aTimeStamp) override {
769 mTouchResampler.NotifyFrame(aTimeStamp - TimeDuration::FromMilliseconds(
770 kTouchResampleVsyncAdjustMs));
771 ConsumeMotionEventsFromResampler();
774 void ConsumeMotionEventsFromResampler() {
775 auto outgoing = mTouchResampler.ConsumeOutgoingEvents();
776 while (!outgoing.empty()) {
777 auto outgoingEvent = std::move(outgoing.front());
778 outgoing.pop();
779 java::GeckoResult::GlobalRef returnResult;
780 if (outgoingEvent.mEventId) {
781 // Look up the GeckoResult for this event.
782 // The outgoing events from the resampler are in the same order as the
783 // original events, and no event IDs are skipped.
784 MOZ_RELEASE_ASSERT(!mPendingMotionEventReturnResults.empty());
785 auto pair = mPendingMotionEventReturnResults.front();
786 mPendingMotionEventReturnResults.pop();
787 MOZ_RELEASE_ASSERT(pair.first == *outgoingEvent.mEventId);
788 returnResult = pair.second;
790 FinishHandlingMotionEvent(std::move(outgoingEvent.mEvent),
791 java::GeckoResult::LocalRef(returnResult));
795 void FinishHandlingMotionEvent(MultiTouchInput&& aInput,
796 java::GeckoResult::LocalRef&& aReturnResult) {
797 RefPtr<IAPZCTreeManager> controller;
799 if (auto window = mWindow.Access()) {
800 nsWindow* gkWindow = window->GetNsWindow();
801 if (gkWindow) {
802 controller = gkWindow->mAPZC;
806 if (!controller) {
807 if (aReturnResult) {
808 aReturnResult->Complete(java::PanZoomController::InputResultDetail::New(
809 INPUT_RESULT_UNHANDLED,
810 java::PanZoomController::SCROLLABLE_FLAG_NONE,
811 java::PanZoomController::OVERSCROLL_FLAG_NONE));
813 return;
816 APZInputBridge::InputBlockCallback callback;
817 if (aReturnResult) {
818 callback = [aReturnResult = java::GeckoResult::GlobalRef(aReturnResult)](
819 uint64_t aInputBlockId,
820 const APZHandledResult& aHandledResult) {
821 aReturnResult->Complete(ConvertAPZHandledResult(aHandledResult));
824 APZEventResult result = controller->InputBridge()->ReceiveInputEvent(
825 aInput, std::move(callback));
827 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
828 if (aReturnResult) {
829 aReturnResult->Complete(java::PanZoomController::InputResultDetail::New(
830 INPUT_RESULT_IGNORED, java::PanZoomController::SCROLLABLE_FLAG_NONE,
831 java::PanZoomController::OVERSCROLL_FLAG_NONE));
833 return;
836 // Dispatch APZ input event on Gecko thread.
837 PostInputEvent([aInput, result](nsWindow* window) {
838 WidgetTouchEvent touchEvent = aInput.ToWidgetEvent(window);
839 window->ProcessUntransformedAPZEvent(&touchEvent, result);
840 window->DispatchHitTest(touchEvent);
843 if (aReturnResult && result.GetHandledResult() != Nothing()) {
844 // We know conclusively that the root APZ handled this or not and
845 // don't need to do any more work.
846 switch (result.GetStatus()) {
847 case nsEventStatus_eIgnore:
848 aReturnResult->Complete(
849 java::PanZoomController::InputResultDetail::New(
850 INPUT_RESULT_UNHANDLED,
851 java::PanZoomController::SCROLLABLE_FLAG_NONE,
852 java::PanZoomController::OVERSCROLL_FLAG_NONE));
853 break;
854 case nsEventStatus_eConsumeDoDefault:
855 aReturnResult->Complete(
856 ConvertAPZHandledResult(result.GetHandledResult().value()));
857 break;
858 default:
859 MOZ_ASSERT_UNREACHABLE("Unexpected nsEventStatus");
860 aReturnResult->Complete(
861 java::PanZoomController::InputResultDetail::New(
862 INPUT_RESULT_UNHANDLED,
863 java::PanZoomController::SCROLLABLE_FLAG_NONE,
864 java::PanZoomController::OVERSCROLL_FLAG_NONE));
865 break;
871 NS_IMPL_ISUPPORTS(AndroidView, nsIAndroidEventDispatcher, nsIAndroidView)
873 nsresult AndroidView::GetInitData(JSContext* aCx, JS::MutableHandleValue aOut) {
874 if (!mInitData) {
875 aOut.setNull();
876 return NS_OK;
879 return widget::EventDispatcher::UnboxBundle(aCx, mInitData, aOut);
883 * Compositor has some unique requirements for its native calls, so make it
884 * separate from GeckoViewSupport.
886 class LayerViewSupport final
887 : public GeckoSession::Compositor::Natives<LayerViewSupport> {
888 WindowPtr mWindow;
889 GeckoSession::Compositor::WeakRef mCompositor;
890 Atomic<bool, ReleaseAcquire> mCompositorPaused;
891 java::sdk::Surface::GlobalRef mSurface;
892 // Used to communicate with the gecko compositor from the UI thread.
893 // Set in NotifyCompositorCreated and cleared in NotifyCompositorSessionLost.
894 RefPtr<UiCompositorControllerChild> mUiCompositorControllerChild;
896 Maybe<uint32_t> mDefaultClearColor;
898 struct CaptureRequest {
899 explicit CaptureRequest() : mResult(nullptr) {}
900 explicit CaptureRequest(java::GeckoResult::GlobalRef aResult,
901 java::sdk::Bitmap::GlobalRef aBitmap,
902 const ScreenRect& aSource,
903 const IntSize& aOutputSize)
904 : mResult(aResult),
905 mBitmap(aBitmap),
906 mSource(aSource),
907 mOutputSize(aOutputSize) {}
909 // where to send the pixels
910 java::GeckoResult::GlobalRef mResult;
912 // where to store the pixels
913 java::sdk::Bitmap::GlobalRef mBitmap;
915 ScreenRect mSource;
917 IntSize mOutputSize;
919 std::queue<CaptureRequest> mCapturePixelsResults;
921 // In order to use Event::HasSameTypeAs in PostTo(), we cannot make
922 // LayerViewEvent a template because each template instantiation is
923 // a different type. So implement LayerViewEvent as a ProxyEvent.
924 class LayerViewEvent final : public nsAppShell::ProxyEvent {
925 using Event = nsAppShell::Event;
927 public:
928 static UniquePtr<Event> MakeEvent(UniquePtr<Event>&& event) {
929 return MakeUnique<LayerViewEvent>(std::move(event));
932 explicit LayerViewEvent(UniquePtr<Event>&& event)
933 : nsAppShell::ProxyEvent(std::move(event)) {}
935 void PostTo(LinkedList<Event>& queue) override {
936 // Give priority to compositor events, but keep in order with
937 // existing compositor events.
938 nsAppShell::Event* event = queue.getFirst();
939 while (event && event->HasSameTypeAs(this)) {
940 event = event->getNext();
942 if (event) {
943 event->setPrevious(this);
944 } else {
945 queue.insertBack(this);
950 public:
951 typedef GeckoSession::Compositor::Natives<LayerViewSupport> Base;
953 LayerViewSupport(WindowPtr aWindow,
954 const GeckoSession::Compositor::LocalRef& aInstance)
955 : mWindow(aWindow), mCompositor(aInstance), mCompositorPaused(true) {
956 #if defined(DEBUG)
957 auto win(mWindow.Access());
958 MOZ_ASSERT(!!win);
959 #endif // defined(DEBUG)
962 ~LayerViewSupport() {}
964 using Base::AttachNative;
965 using Base::DisposeNative;
967 void OnWeakNonIntrusiveDetach(already_AddRefed<Runnable> aDisposer) {
968 RefPtr<Runnable> disposer = aDisposer;
969 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
970 GeckoSession::Compositor::GlobalRef compositor(mCompositor);
971 if (!compositor) {
972 return;
975 uiThread->Dispatch(NS_NewRunnableFunction(
976 "LayerViewSupport::OnWeakNonIntrusiveDetach",
977 [compositor, disposer = std::move(disposer),
978 results = &mCapturePixelsResults, window = mWindow]() mutable {
979 if (auto accWindow = window.Access()) {
980 while (!results->empty()) {
981 auto aResult =
982 java::GeckoResult::LocalRef(results->front().mResult);
983 if (aResult) {
984 aResult->CompleteExceptionally(
985 java::sdk::IllegalStateException::New(
986 "The compositor has detached from the session")
987 .Cast<jni::Throwable>());
989 results->pop();
993 compositor->OnCompositorDetached();
994 disposer->Run();
995 }));
999 const GeckoSession::Compositor::Ref& GetJavaCompositor() const {
1000 return mCompositor;
1003 bool CompositorPaused() const { return mCompositorPaused; }
1005 /// Called from the main thread whenever the compositor has been
1006 /// (re)initialized.
1007 void NotifyCompositorCreated(
1008 RefPtr<UiCompositorControllerChild> aUiCompositorControllerChild) {
1009 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1010 mUiCompositorControllerChild = aUiCompositorControllerChild;
1012 if (auto window{mWindow.Access()}) {
1013 nsWindow* gkWindow = window->GetNsWindow();
1014 if (gkWindow) {
1015 mUiCompositorControllerChild->OnCompositorSurfaceChanged(
1016 gkWindow->mWidgetId, mSurface);
1020 if (mDefaultClearColor) {
1021 mUiCompositorControllerChild->SetDefaultClearColor(*mDefaultClearColor);
1024 if (!mCompositorPaused) {
1025 mUiCompositorControllerChild->Resume();
1029 /// Called from the main thread whenever the compositor has been destroyed.
1030 void NotifyCompositorSessionLost() {
1031 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1032 mUiCompositorControllerChild = nullptr;
1034 if (auto window = mWindow.Access()) {
1035 while (!mCapturePixelsResults.empty()) {
1036 auto result =
1037 java::GeckoResult::LocalRef(mCapturePixelsResults.front().mResult);
1038 if (result) {
1039 result->CompleteExceptionally(
1040 java::sdk::IllegalStateException::New(
1041 "Compositor session lost during screen pixels request")
1042 .Cast<jni::Throwable>());
1044 mCapturePixelsResults.pop();
1049 java::sdk::Surface::Param GetSurface() { return mSurface; }
1051 private:
1052 already_AddRefed<DataSourceSurface> FlipScreenPixels(
1053 Shmem& aMem, const ScreenIntSize& aInSize, const ScreenRect& aInRegion,
1054 const IntSize& aOutSize) {
1055 RefPtr<gfx::DataSourceSurface> image =
1056 gfx::Factory::CreateWrappingDataSourceSurface(
1057 aMem.get<uint8_t>(),
1058 StrideForFormatAndWidth(SurfaceFormat::B8G8R8A8, aInSize.width),
1059 IntSize(aInSize.width, aInSize.height), SurfaceFormat::B8G8R8A8);
1060 RefPtr<gfx::DrawTarget> drawTarget =
1061 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
1062 aOutSize, SurfaceFormat::B8G8R8A8);
1063 if (!drawTarget) {
1064 return nullptr;
1067 drawTarget->SetTransform(Matrix::Scaling(1.0, -1.0) *
1068 Matrix::Translation(0, aOutSize.height));
1070 gfx::Rect srcRect(aInRegion.x,
1071 (aInSize.height - aInRegion.height) - aInRegion.y,
1072 aInRegion.width, aInRegion.height);
1073 gfx::Rect destRect(0, 0, aOutSize.width, aOutSize.height);
1074 drawTarget->DrawSurface(image, destRect, srcRect);
1076 RefPtr<gfx::SourceSurface> snapshot = drawTarget->Snapshot();
1077 RefPtr<gfx::DataSourceSurface> data = snapshot->GetDataSurface();
1078 return data.forget();
1082 * Compositor methods
1084 public:
1085 void AttachNPZC(jni::Object::Param aNPZC) {
1086 MOZ_ASSERT(NS_IsMainThread());
1087 MOZ_ASSERT(aNPZC);
1089 auto locked(mWindow.Access());
1090 if (!locked) {
1091 return; // Already shut down.
1094 nsWindow* gkWindow = locked->GetNsWindow();
1096 // We can have this situation if we get two GeckoViewSupport::Transfer()
1097 // called before the first AttachNPZC() gets here. Just detach the current
1098 // instance since that's what happens in GeckoViewSupport::Transfer() as
1099 // well.
1100 gkWindow->mNPZCSupport.Detach();
1102 auto npzc = java::PanZoomController::NativeProvider::LocalRef(
1103 jni::GetGeckoThreadEnv(),
1104 java::PanZoomController::NativeProvider::Ref::From(aNPZC));
1105 gkWindow->mNPZCSupport =
1106 jni::NativeWeakPtrHolder<NPZCSupport>::Attach(npzc, mWindow, npzc);
1108 DispatchToUiThread(
1109 "LayerViewSupport::AttachNPZC",
1110 [npzc = java::PanZoomController::NativeProvider::GlobalRef(npzc)] {
1111 npzc->SetAttached(true);
1115 void OnBoundsChanged(int32_t aLeft, int32_t aTop, int32_t aWidth,
1116 int32_t aHeight) {
1117 MOZ_ASSERT(NS_IsMainThread());
1118 auto acc = mWindow.Access();
1119 if (!acc) {
1120 return; // Already shut down.
1123 nsWindow* gkWindow = acc->GetNsWindow();
1124 if (!gkWindow) {
1125 return;
1128 gkWindow->Resize(aLeft, aTop, aWidth, aHeight, /* repaint */ false);
1131 void NotifyMemoryPressure() {
1132 MOZ_ASSERT(NS_IsMainThread());
1133 auto acc = mWindow.Access();
1134 if (!acc) {
1135 return; // Already shut down.
1138 nsWindow* gkWindow = acc->GetNsWindow();
1139 if (!gkWindow || !gkWindow->mCompositorBridgeChild) {
1140 return;
1143 gkWindow->mCompositorBridgeChild->SendNotifyMemoryPressure();
1146 void SetDynamicToolbarMaxHeight(int32_t aHeight) {
1147 MOZ_ASSERT(NS_IsMainThread());
1148 auto acc = mWindow.Access();
1149 if (!acc) {
1150 return; // Already shut down.
1153 nsWindow* gkWindow = acc->GetNsWindow();
1154 if (!gkWindow) {
1155 return;
1158 gkWindow->UpdateDynamicToolbarMaxHeight(ScreenIntCoord(aHeight));
1161 void SyncPauseCompositor() {
1162 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1164 mCompositorPaused = true;
1166 if (mUiCompositorControllerChild) {
1167 mUiCompositorControllerChild->Pause();
1170 if (auto lock{mWindow.Access()}) {
1171 while (!mCapturePixelsResults.empty()) {
1172 auto result =
1173 java::GeckoResult::LocalRef(mCapturePixelsResults.front().mResult);
1174 if (result) {
1175 result->CompleteExceptionally(
1176 java::sdk::IllegalStateException::New(
1177 "The compositor has detached from the session")
1178 .Cast<jni::Throwable>());
1180 mCapturePixelsResults.pop();
1185 void SyncResumeCompositor() {
1186 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1188 if (mUiCompositorControllerChild) {
1189 mCompositorPaused = false;
1190 mUiCompositorControllerChild->Resume();
1194 void SyncResumeResizeCompositor(
1195 const GeckoSession::Compositor::LocalRef& aObj, int32_t aX, int32_t aY,
1196 int32_t aWidth, int32_t aHeight, jni::Object::Param aSurface) {
1197 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1199 mSurface = java::sdk::Surface::GlobalRef::From(aSurface);
1201 if (mUiCompositorControllerChild) {
1202 if (auto window = mWindow.Access()) {
1203 nsWindow* gkWindow = window->GetNsWindow();
1204 if (gkWindow) {
1205 // Send new Surface to GPU process, if one exists.
1206 mUiCompositorControllerChild->OnCompositorSurfaceChanged(
1207 gkWindow->mWidgetId, mSurface);
1211 mUiCompositorControllerChild->ResumeAndResize(aX, aY, aWidth, aHeight);
1214 mCompositorPaused = false;
1216 class OnResumedEvent : public nsAppShell::Event {
1217 GeckoSession::Compositor::GlobalRef mCompositor;
1219 public:
1220 explicit OnResumedEvent(GeckoSession::Compositor::GlobalRef&& aCompositor)
1221 : mCompositor(std::move(aCompositor)) {}
1223 void Run() override {
1224 MOZ_ASSERT(NS_IsMainThread());
1226 JNIEnv* const env = jni::GetGeckoThreadEnv();
1227 const auto lvsHolder =
1228 GetNative(GeckoSession::Compositor::LocalRef(env, mCompositor));
1230 if (!lvsHolder) {
1231 env->ExceptionClear();
1232 return; // Already shut down.
1235 auto lvs(lvsHolder->Access());
1236 if (!lvs) {
1237 env->ExceptionClear();
1238 return; // Already shut down.
1241 auto win = lvs->mWindow.Access();
1242 if (!win) {
1243 env->ExceptionClear();
1244 return; // Already shut down.
1247 // When we get here, the compositor has already been told to
1248 // resume. This means it's now safe for layer updates to occur.
1249 // Since we might have prevented one or more draw events from
1250 // occurring while the compositor was paused, we need to
1251 // schedule a draw event now.
1252 if (!lvs->mCompositorPaused) {
1253 nsWindow* const gkWindow = win->GetNsWindow();
1254 if (gkWindow) {
1255 gkWindow->RedrawAll();
1261 // Use priority queue for timing-sensitive event.
1262 nsAppShell::PostEvent(
1263 MakeUnique<LayerViewEvent>(MakeUnique<OnResumedEvent>(aObj)));
1266 void SyncInvalidateAndScheduleComposite() {
1267 if (!mUiCompositorControllerChild) {
1268 return;
1271 if (AndroidBridge::IsJavaUiThread()) {
1272 mUiCompositorControllerChild->InvalidateAndRender();
1273 return;
1276 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
1277 uiThread->Dispatch(NewRunnableMethod<>(
1278 "LayerViewSupport::InvalidateAndRender",
1279 mUiCompositorControllerChild,
1280 &UiCompositorControllerChild::InvalidateAndRender),
1281 nsIThread::DISPATCH_NORMAL);
1285 void SetMaxToolbarHeight(int32_t aHeight) {
1286 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1288 if (mUiCompositorControllerChild) {
1289 mUiCompositorControllerChild->SetMaxToolbarHeight(aHeight);
1293 void SetFixedBottomOffset(int32_t aOffset) {
1294 if (auto acc{mWindow.Access()}) {
1295 nsWindow* gkWindow = acc->GetNsWindow();
1296 if (gkWindow) {
1297 gkWindow->UpdateDynamicToolbarOffset(ScreenIntCoord(aOffset));
1301 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
1302 uiThread->Dispatch(NS_NewRunnableFunction(
1303 "LayerViewSupport::SetFixedBottomOffset", [this, offset = aOffset] {
1304 if (mUiCompositorControllerChild) {
1305 mUiCompositorControllerChild->SetFixedBottomOffset(offset);
1307 }));
1311 void SendToolbarAnimatorMessage(int32_t aMessage) {
1312 if (!mUiCompositorControllerChild) {
1313 return;
1316 if (AndroidBridge::IsJavaUiThread()) {
1317 mUiCompositorControllerChild->ToolbarAnimatorMessageFromUI(aMessage);
1318 return;
1321 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
1322 uiThread->Dispatch(
1323 NewRunnableMethod<int32_t>(
1324 "LayerViewSupport::ToolbarAnimatorMessageFromUI",
1325 mUiCompositorControllerChild,
1326 &UiCompositorControllerChild::ToolbarAnimatorMessageFromUI,
1327 aMessage),
1328 nsIThread::DISPATCH_NORMAL);
1332 void RecvToolbarAnimatorMessage(int32_t aMessage) {
1333 auto compositor = GeckoSession::Compositor::LocalRef(mCompositor);
1334 if (compositor) {
1335 compositor->RecvToolbarAnimatorMessage(aMessage);
1339 void SetDefaultClearColor(int32_t aColor) {
1340 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1341 mDefaultClearColor = Some((uint32_t)aColor);
1342 if (mUiCompositorControllerChild) {
1343 mUiCompositorControllerChild->SetDefaultClearColor((uint32_t)aColor);
1347 void RequestScreenPixels(jni::Object::Param aResult,
1348 jni::Object::Param aTarget, int32_t aXOffset,
1349 int32_t aYOffset, int32_t aSrcWidth,
1350 int32_t aSrcHeight, int32_t aOutWidth,
1351 int32_t aOutHeight) {
1352 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1353 auto result = java::GeckoResult::LocalRef(aResult);
1355 if (!mUiCompositorControllerChild) {
1356 if (result) {
1357 if (auto window = mWindow.Access()) {
1358 result->CompleteExceptionally(
1359 java::sdk::IllegalStateException::New(
1360 "Compositor session lost prior to screen pixels request")
1361 .Cast<jni::Throwable>());
1364 return;
1367 int size = 0;
1368 if (auto window = mWindow.Access()) {
1369 mCapturePixelsResults.push(CaptureRequest(
1370 java::GeckoResult::GlobalRef(result),
1371 java::sdk::Bitmap::GlobalRef(java::sdk::Bitmap::LocalRef(aTarget)),
1372 ScreenRect(aXOffset, aYOffset, aSrcWidth, aSrcHeight),
1373 IntSize(aOutWidth, aOutHeight)));
1374 size = mCapturePixelsResults.size();
1377 if (size == 1) {
1378 mUiCompositorControllerChild->RequestScreenPixels();
1382 void RecvScreenPixels(Shmem&& aMem, const ScreenIntSize& aSize,
1383 bool aNeedsYFlip) {
1384 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1385 CaptureRequest request;
1386 java::GeckoResult::LocalRef result = nullptr;
1387 java::sdk::Bitmap::LocalRef bitmap = nullptr;
1388 if (auto window = mWindow.Access()) {
1389 // The result might have been already rejected if the compositor was
1390 // detached from the session
1391 if (!mCapturePixelsResults.empty()) {
1392 request = mCapturePixelsResults.front();
1393 result = java::GeckoResult::LocalRef(request.mResult);
1394 bitmap = java::sdk::Bitmap::LocalRef(request.mBitmap);
1395 mCapturePixelsResults.pop();
1399 if (result) {
1400 if (bitmap) {
1401 RefPtr<DataSourceSurface> surf;
1402 if (aNeedsYFlip) {
1403 surf = FlipScreenPixels(aMem, aSize, request.mSource,
1404 request.mOutputSize);
1405 } else {
1406 surf = gfx::Factory::CreateWrappingDataSourceSurface(
1407 aMem.get<uint8_t>(),
1408 StrideForFormatAndWidth(SurfaceFormat::B8G8R8A8, aSize.width),
1409 IntSize(aSize.width, aSize.height), SurfaceFormat::B8G8R8A8);
1411 if (surf) {
1412 DataSourceSurface::ScopedMap smap(surf, DataSourceSurface::READ);
1413 auto pixels = mozilla::jni::ByteBuffer::New(
1414 reinterpret_cast<int8_t*>(smap.GetData()),
1415 smap.GetStride() * request.mOutputSize.height);
1416 bitmap->CopyPixelsFromBuffer(pixels);
1417 result->Complete(bitmap);
1418 } else {
1419 result->CompleteExceptionally(
1420 java::sdk::IllegalStateException::New(
1421 "Failed to create flipped snapshot surface (probably out of "
1422 "memory)")
1423 .Cast<jni::Throwable>());
1425 } else {
1426 result->CompleteExceptionally(java::sdk::IllegalArgumentException::New(
1427 "No target bitmap argument provided")
1428 .Cast<jni::Throwable>());
1432 // Pixels have been copied, so Dealloc Shmem
1433 if (mUiCompositorControllerChild) {
1434 mUiCompositorControllerChild->DeallocPixelBuffer(aMem);
1436 if (auto window = mWindow.Access()) {
1437 if (!mCapturePixelsResults.empty()) {
1438 mUiCompositorControllerChild->RequestScreenPixels();
1444 void EnableLayerUpdateNotifications(bool aEnable) {
1445 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1446 if (mUiCompositorControllerChild) {
1447 mUiCompositorControllerChild->EnableLayerUpdateNotifications(aEnable);
1451 void OnSafeAreaInsetsChanged(int32_t aTop, int32_t aRight, int32_t aBottom,
1452 int32_t aLeft) {
1453 MOZ_ASSERT(NS_IsMainThread());
1454 auto win(mWindow.Access());
1455 if (!win) {
1456 return; // Already shut down.
1459 nsWindow* gkWindow = win->GetNsWindow();
1460 if (!gkWindow) {
1461 return;
1464 ScreenIntMargin safeAreaInsets(aTop, aRight, aBottom, aLeft);
1465 gkWindow->UpdateSafeAreaInsets(safeAreaInsets);
1469 GeckoViewSupport::~GeckoViewSupport() {
1470 if (mWindow) {
1471 mWindow->DetachNatives();
1475 /* static */
1476 void GeckoViewSupport::Open(
1477 const jni::Class::LocalRef& aCls, GeckoSession::Window::Param aWindow,
1478 jni::Object::Param aQueue, jni::Object::Param aCompositor,
1479 jni::Object::Param aDispatcher, jni::Object::Param aSessionAccessibility,
1480 jni::Object::Param aInitData, jni::String::Param aId,
1481 jni::String::Param aChromeURI, bool aPrivateMode) {
1482 MOZ_ASSERT(NS_IsMainThread());
1484 AUTO_PROFILER_LABEL("mozilla::widget::GeckoViewSupport::Open", OTHER);
1486 // We'll need gfxPlatform to be initialized to create a compositor later.
1487 // Might as well do that now so that the GPU process launch can get a head
1488 // start.
1489 gfxPlatform::GetPlatform();
1491 nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID);
1492 MOZ_RELEASE_ASSERT(ww);
1494 nsAutoCString url;
1495 if (aChromeURI) {
1496 url = aChromeURI->ToCString();
1497 } else {
1498 nsresult rv = Preferences::GetCString("toolkit.defaultChromeURI", url);
1499 if (NS_FAILED(rv)) {
1500 url = "chrome://geckoview/content/geckoview.xhtml"_ns;
1504 // Prepare an nsIAndroidView to pass as argument to the window.
1505 RefPtr<AndroidView> androidView = new AndroidView();
1506 androidView->mEventDispatcher->Attach(
1507 java::EventDispatcher::Ref::From(aDispatcher), nullptr);
1508 androidView->mInitData = java::GeckoBundle::Ref::From(aInitData);
1510 nsAutoCString chromeFlags("chrome,dialog=0,remote,resizable,scrollbars");
1511 if (aPrivateMode) {
1512 chromeFlags += ",private";
1514 nsCOMPtr<mozIDOMWindowProxy> domWindow;
1515 ww->OpenWindow(nullptr, url, nsDependentCString(aId->ToCString().get()),
1516 chromeFlags, androidView, getter_AddRefs(domWindow));
1517 MOZ_RELEASE_ASSERT(domWindow);
1519 nsCOMPtr<nsPIDOMWindowOuter> pdomWindow = nsPIDOMWindowOuter::From(domWindow);
1520 const RefPtr<nsWindow> window = nsWindow::From(pdomWindow);
1521 MOZ_ASSERT(window);
1523 // Attach a new GeckoView support object to the new window.
1524 GeckoSession::Window::LocalRef sessionWindow(aCls.Env(), aWindow);
1525 auto weakGeckoViewSupport =
1526 jni::NativeWeakPtrHolder<GeckoViewSupport>::Attach(
1527 sessionWindow, window, sessionWindow, pdomWindow);
1529 window->mGeckoViewSupport = weakGeckoViewSupport;
1530 window->mAndroidView = androidView;
1532 // Attach other session support objects.
1533 { // Scope for gvsAccess
1534 auto gvsAccess = weakGeckoViewSupport.Access();
1535 MOZ_ASSERT(gvsAccess);
1537 gvsAccess->Transfer(sessionWindow, aQueue, aCompositor, aDispatcher,
1538 aSessionAccessibility, aInitData);
1541 if (window->mWidgetListener) {
1542 nsCOMPtr<nsIAppWindow> appWindow(window->mWidgetListener->GetAppWindow());
1543 if (appWindow) {
1544 // Our window is not intrinsically sized, so tell AppWindow to
1545 // not set a size for us.
1546 appWindow->SetIntrinsicallySized(false);
1551 void GeckoViewSupport::Close() {
1552 if (mWindow) {
1553 if (mWindow->mAndroidView) {
1554 mWindow->mAndroidView->mEventDispatcher->Detach();
1556 mWindow = nullptr;
1559 if (!mDOMWindow) {
1560 return;
1563 mDOMWindow->ForceClose();
1564 mDOMWindow = nullptr;
1565 mGeckoViewWindow = nullptr;
1568 void GeckoViewSupport::Transfer(const GeckoSession::Window::LocalRef& inst,
1569 jni::Object::Param aQueue,
1570 jni::Object::Param aCompositor,
1571 jni::Object::Param aDispatcher,
1572 jni::Object::Param aSessionAccessibility,
1573 jni::Object::Param aInitData) {
1574 mWindow->mNPZCSupport.Detach();
1576 auto compositor = GeckoSession::Compositor::LocalRef(
1577 inst.Env(), GeckoSession::Compositor::Ref::From(aCompositor));
1579 bool attachLvs;
1580 { // Scope for lvsAccess
1581 auto lvsAccess{mWindow->mLayerViewSupport.Access()};
1582 // If we do not yet have mLayerViewSupport, or if the compositor has
1583 // changed, then we must attach a new one.
1584 attachLvs = !lvsAccess || lvsAccess->GetJavaCompositor() != compositor;
1587 if (attachLvs) {
1588 mWindow->mLayerViewSupport =
1589 jni::NativeWeakPtrHolder<LayerViewSupport>::Attach(
1590 compositor, mWindow->mGeckoViewSupport, compositor);
1592 if (RefPtr<UiCompositorControllerChild> uiCompositorController =
1593 mWindow->GetUiCompositorControllerChild()) {
1594 DispatchToUiThread(
1595 "LayerViewSupport::NotifyCompositorCreated",
1596 [lvs = mWindow->mLayerViewSupport, uiCompositorController] {
1597 if (auto lvsAccess{lvs.Access()}) {
1598 lvsAccess->NotifyCompositorCreated(uiCompositorController);
1604 MOZ_ASSERT(mWindow->mAndroidView);
1605 mWindow->mAndroidView->mEventDispatcher->Attach(
1606 java::EventDispatcher::Ref::From(aDispatcher), mDOMWindow);
1608 mWindow->mSessionAccessibility.Detach();
1609 if (aSessionAccessibility) {
1610 AttachAccessibility(inst, aSessionAccessibility);
1613 if (mIsReady) {
1614 // We're in a transfer; update init-data and notify JS code.
1615 mWindow->mAndroidView->mInitData = java::GeckoBundle::Ref::From(aInitData);
1616 OnReady(aQueue);
1617 mWindow->mAndroidView->mEventDispatcher->Dispatch(
1618 u"GeckoView:UpdateInitData");
1621 DispatchToUiThread("GeckoViewSupport::Transfer",
1622 [compositor = GeckoSession::Compositor::GlobalRef(
1623 compositor)] { compositor->OnCompositorAttached(); });
1626 void GeckoViewSupport::AttachEditable(
1627 const GeckoSession::Window::LocalRef& inst,
1628 jni::Object::Param aEditableParent) {
1629 if (auto win{mWindow->mEditableSupport.Access()}) {
1630 win->TransferParent(aEditableParent);
1631 } else {
1632 auto editableChild = java::GeckoEditableChild::New(aEditableParent,
1633 /* default */ true);
1634 mWindow->mEditableSupport =
1635 jni::NativeWeakPtrHolder<GeckoEditableSupport>::Attach(
1636 editableChild, mWindow->mGeckoViewSupport, editableChild);
1639 mWindow->mEditableParent = aEditableParent;
1642 void GeckoViewSupport::AttachAccessibility(
1643 const GeckoSession::Window::LocalRef& inst,
1644 jni::Object::Param aSessionAccessibility) {
1645 java::SessionAccessibility::NativeProvider::LocalRef sessionAccessibility(
1646 inst.Env());
1647 sessionAccessibility = java::SessionAccessibility::NativeProvider::Ref::From(
1648 aSessionAccessibility);
1650 mWindow->mSessionAccessibility =
1651 jni::NativeWeakPtrHolder<a11y::SessionAccessibility>::Attach(
1652 sessionAccessibility, mWindow->mGeckoViewSupport,
1653 sessionAccessibility);
1656 auto GeckoViewSupport::OnLoadRequest(mozilla::jni::String::Param aUri,
1657 int32_t aWindowType, int32_t aFlags,
1658 mozilla::jni::String::Param aTriggeringUri,
1659 bool aHasUserGesture,
1660 bool aIsTopLevel) const
1661 -> java::GeckoResult::LocalRef {
1662 GeckoSession::Window::LocalRef window(mGeckoViewWindow);
1663 if (!window) {
1664 return nullptr;
1666 return window->OnLoadRequest(aUri, aWindowType, aFlags, aTriggeringUri,
1667 aHasUserGesture, aIsTopLevel);
1670 void GeckoViewSupport::OnShowDynamicToolbar() const {
1671 GeckoSession::Window::LocalRef window(mGeckoViewWindow);
1672 if (!window) {
1673 return;
1676 window->OnShowDynamicToolbar();
1679 void GeckoViewSupport::OnReady(jni::Object::Param aQueue) {
1680 GeckoSession::Window::LocalRef window(mGeckoViewWindow);
1681 if (!window) {
1682 return;
1684 window->OnReady(aQueue);
1685 mIsReady = true;
1688 void GeckoViewSupport::PassExternalResponse(
1689 java::WebResponse::Param aResponse) {
1690 GeckoSession::Window::LocalRef window(mGeckoViewWindow);
1691 if (!window) {
1692 return;
1695 auto response = java::WebResponse::GlobalRef(aResponse);
1697 DispatchToUiThread("GeckoViewSupport::PassExternalResponse",
1698 [window = java::GeckoSession::Window::GlobalRef(window),
1699 response] { window->PassExternalWebResponse(response); });
1702 } // namespace widget
1703 } // namespace mozilla
1705 void nsWindow::InitNatives() {
1706 jni::InitConversionStatics();
1707 mozilla::widget::GeckoViewSupport::Base::Init();
1708 mozilla::widget::LayerViewSupport::Init();
1709 mozilla::widget::NPZCSupport::Init();
1711 mozilla::widget::GeckoEditableSupport::Init();
1712 a11y::SessionAccessibility::Init();
1715 void nsWindow::DetachNatives() {
1716 MOZ_ASSERT(NS_IsMainThread());
1717 mEditableSupport.Detach();
1718 mNPZCSupport.Detach();
1719 mLayerViewSupport.Detach();
1720 mSessionAccessibility.Detach();
1723 /* static */
1724 already_AddRefed<nsWindow> nsWindow::From(nsPIDOMWindowOuter* aDOMWindow) {
1725 nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(aDOMWindow);
1726 return From(widget);
1729 /* static */
1730 already_AddRefed<nsWindow> nsWindow::From(nsIWidget* aWidget) {
1731 // `widget` may be one of several different types in the parent
1732 // process, including the Android nsWindow, PuppetWidget, etc. To
1733 // ensure that the cast to the Android nsWindow is valid, we check that the
1734 // widget is a top-level window and that its NS_NATIVE_WIDGET value is
1735 // non-null, which is not the case for non-native widgets like
1736 // PuppetWidget.
1737 if (aWidget && aWidget->WindowType() == nsWindowType::eWindowType_toplevel &&
1738 aWidget->GetNativeData(NS_NATIVE_WIDGET) == aWidget) {
1739 RefPtr<nsWindow> window = static_cast<nsWindow*>(aWidget);
1740 return window.forget();
1742 return nullptr;
1745 nsWindow* nsWindow::TopWindow() {
1746 if (!gTopLevelWindows.IsEmpty()) return gTopLevelWindows[0];
1747 return nullptr;
1750 void nsWindow::LogWindow(nsWindow* win, int index, int indent) {
1751 #if defined(DEBUG) || defined(FORCE_ALOG)
1752 char spaces[] = " ";
1753 spaces[indent < 20 ? indent : 20] = 0;
1754 ALOG("%s [% 2d] 0x%p [parent 0x%p] [% 3d,% 3dx% 3d,% 3d] vis %d type %d",
1755 spaces, index, win, win->mParent, win->mBounds.x, win->mBounds.y,
1756 win->mBounds.width, win->mBounds.height, win->mIsVisible,
1757 win->mWindowType);
1758 #endif
1761 void nsWindow::DumpWindows() { DumpWindows(gTopLevelWindows); }
1763 void nsWindow::DumpWindows(const nsTArray<nsWindow*>& wins, int indent) {
1764 for (uint32_t i = 0; i < wins.Length(); ++i) {
1765 nsWindow* w = wins[i];
1766 LogWindow(w, i, indent);
1767 DumpWindows(w->mChildren, indent + 1);
1771 nsWindow::nsWindow()
1772 : mWidgetId(++sWidgetId),
1773 mIsVisible(false),
1774 mParent(nullptr),
1775 mDynamicToolbarMaxHeight(0),
1776 mIsFullScreen(false),
1777 mCompositorWidgetDelegate(nullptr) {}
1779 nsWindow::~nsWindow() {
1780 gTopLevelWindows.RemoveElement(this);
1781 ALOG("nsWindow %p destructor", (void*)this);
1782 // The mCompositorSession should have been cleaned up in nsWindow::Destroy()
1783 // DestroyLayerManager() will call DestroyCompositor() which will crash if
1784 // called from nsBaseWidget destructor. See Bug 1392705
1785 MOZ_ASSERT(!mCompositorSession);
1788 bool nsWindow::IsTopLevel() {
1789 return mWindowType == eWindowType_toplevel ||
1790 mWindowType == eWindowType_dialog ||
1791 mWindowType == eWindowType_invisible;
1794 nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
1795 const LayoutDeviceIntRect& aRect,
1796 nsWidgetInitData* aInitData) {
1797 ALOG("nsWindow[%p]::Create %p [%d %d %d %d]", (void*)this, (void*)aParent,
1798 aRect.x, aRect.y, aRect.width, aRect.height);
1800 nsWindow* parent = (nsWindow*)aParent;
1801 if (aNativeParent) {
1802 if (parent) {
1803 ALOG(
1804 "Ignoring native parent on Android window [%p], "
1805 "since parent was specified (%p %p)",
1806 (void*)this, (void*)aNativeParent, (void*)aParent);
1807 } else {
1808 parent = (nsWindow*)aNativeParent;
1812 // A default size of 1x1 confuses MobileViewportManager, so
1813 // use 0x0 instead. This is also a little more fitting since
1814 // we don't yet have a surface yet (and therefore a valid size)
1815 // and 0x0 is usually recognized as invalid.
1816 LayoutDeviceIntRect rect = aRect;
1817 if (aRect.width == 1 && aRect.height == 1) {
1818 rect.width = 0;
1819 rect.height = 0;
1822 mBounds = rect;
1823 SetSizeConstraints(SizeConstraints());
1825 BaseCreate(nullptr, aInitData);
1827 NS_ASSERTION(IsTopLevel() || parent,
1828 "non-top-level window doesn't have a parent!");
1830 if (IsTopLevel()) {
1831 gTopLevelWindows.AppendElement(this);
1833 } else if (parent) {
1834 parent->mChildren.AppendElement(this);
1835 mParent = parent;
1838 #ifdef DEBUG_ANDROID_WIDGET
1839 DumpWindows();
1840 #endif
1842 return NS_OK;
1845 void nsWindow::Destroy() {
1846 nsBaseWidget::mOnDestroyCalled = true;
1848 // Disassociate our native object from GeckoView.
1849 mGeckoViewSupport.Detach();
1851 // Stuff below may release the last ref to this
1852 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
1854 while (mChildren.Length()) {
1855 // why do we still have children?
1856 ALOG("### Warning: Destroying window %p and reparenting child %p to null!",
1857 (void*)this, (void*)mChildren[0]);
1858 mChildren[0]->SetParent(nullptr);
1861 // Ensure the compositor has been shutdown before this nsWindow is potentially
1862 // deleted
1863 nsBaseWidget::DestroyCompositor();
1865 nsBaseWidget::Destroy();
1867 if (IsTopLevel()) gTopLevelWindows.RemoveElement(this);
1869 SetParent(nullptr);
1871 nsBaseWidget::OnDestroy();
1873 #ifdef DEBUG_ANDROID_WIDGET
1874 DumpWindows();
1875 #endif
1878 mozilla::widget::EventDispatcher* nsWindow::GetEventDispatcher() const {
1879 if (mAndroidView) {
1880 return mAndroidView->mEventDispatcher;
1882 return nullptr;
1885 void nsWindow::RedrawAll() {
1886 if (mAttachedWidgetListener) {
1887 mAttachedWidgetListener->RequestRepaint();
1888 } else if (mWidgetListener) {
1889 mWidgetListener->RequestRepaint();
1893 RefPtr<UiCompositorControllerChild> nsWindow::GetUiCompositorControllerChild() {
1894 return mCompositorSession
1895 ? mCompositorSession->GetUiCompositorControllerChild()
1896 : nullptr;
1899 mozilla::layers::LayersId nsWindow::GetRootLayerId() const {
1900 return mCompositorSession ? mCompositorSession->RootLayerTreeId()
1901 : mozilla::layers::LayersId{0};
1904 void nsWindow::OnGeckoViewReady() {
1905 auto acc(mGeckoViewSupport.Access());
1906 if (!acc) {
1907 return;
1910 acc->OnReady();
1913 void nsWindow::SetParent(nsIWidget* aNewParent) {
1914 if ((nsIWidget*)mParent == aNewParent) return;
1916 // If we had a parent before, remove ourselves from its list of
1917 // children.
1918 if (mParent) mParent->mChildren.RemoveElement(this);
1920 mParent = (nsWindow*)aNewParent;
1922 if (mParent) mParent->mChildren.AppendElement(this);
1924 // if we are now in the toplevel window's hierarchy, schedule a redraw
1925 if (FindTopLevel() == nsWindow::TopWindow()) RedrawAll();
1928 nsIWidget* nsWindow::GetParent() { return mParent; }
1930 RefPtr<MozPromise<bool, bool, false>> nsWindow::OnLoadRequest(
1931 nsIURI* aUri, int32_t aWindowType, int32_t aFlags,
1932 nsIPrincipal* aTriggeringPrincipal, bool aHasUserGesture,
1933 bool aIsTopLevel) {
1934 auto geckoViewSupport(mGeckoViewSupport.Access());
1935 if (!geckoViewSupport) {
1936 return MozPromise<bool, bool, false>::CreateAndResolve(false, __func__);
1939 nsAutoCString spec, triggeringSpec;
1940 if (aUri) {
1941 aUri->GetDisplaySpec(spec);
1942 if (aIsTopLevel && mozilla::net::SchemeIsData(aUri) &&
1943 spec.Length() > MAX_TOPLEVEL_DATA_URI_LEN) {
1944 return MozPromise<bool, bool, false>::CreateAndResolve(false, __func__);
1948 bool isNullPrincipal = false;
1949 if (aTriggeringPrincipal) {
1950 aTriggeringPrincipal->GetIsNullPrincipal(&isNullPrincipal);
1952 if (!isNullPrincipal) {
1953 nsCOMPtr<nsIURI> triggeringUri;
1954 BasePrincipal::Cast(aTriggeringPrincipal)
1955 ->GetURI(getter_AddRefs(triggeringUri));
1956 if (triggeringUri) {
1957 triggeringUri->GetDisplaySpec(triggeringSpec);
1962 auto geckoResult = geckoViewSupport->OnLoadRequest(
1963 spec.get(), aWindowType, aFlags,
1964 isNullPrincipal ? nullptr : triggeringSpec.get(), aHasUserGesture,
1965 aIsTopLevel);
1966 return geckoResult
1967 ? MozPromise<bool, bool, false>::FromGeckoResult(geckoResult)
1968 : nullptr;
1971 float nsWindow::GetDPI() {
1972 float dpi = 160.0f;
1974 nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
1975 if (screen) {
1976 screen->GetDpi(&dpi);
1979 return dpi;
1982 double nsWindow::GetDefaultScaleInternal() {
1983 double scale = 1.0f;
1985 nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
1986 if (screen) {
1987 screen->GetContentsScaleFactor(&scale);
1990 return scale;
1993 void nsWindow::Show(bool aState) {
1994 ALOG("nsWindow[%p]::Show %d", (void*)this, aState);
1996 if (mWindowType == eWindowType_invisible) {
1997 ALOG("trying to show invisible window! ignoring..");
1998 return;
2001 if (aState == mIsVisible) return;
2003 mIsVisible = aState;
2005 if (IsTopLevel()) {
2006 // XXX should we bring this to the front when it's shown,
2007 // if it's a toplevel widget?
2009 // XXX we should synthesize a eMouseExitFromWidget (for old top
2010 // window)/eMouseEnterIntoWidget (for new top window) since we need
2011 // to pretend that the top window always has focus. Not sure
2012 // if Show() is the right place to do this, though.
2014 if (aState) {
2015 // It just became visible, so bring it to the front.
2016 BringToFront();
2018 } else if (nsWindow::TopWindow() == this) {
2019 // find the next visible window to show
2020 unsigned int i;
2021 for (i = 1; i < gTopLevelWindows.Length(); i++) {
2022 nsWindow* win = gTopLevelWindows[i];
2023 if (!win->mIsVisible) continue;
2025 win->BringToFront();
2026 break;
2029 } else if (FindTopLevel() == nsWindow::TopWindow()) {
2030 RedrawAll();
2033 #ifdef DEBUG_ANDROID_WIDGET
2034 DumpWindows();
2035 #endif
2038 bool nsWindow::IsVisible() const { return mIsVisible; }
2040 void nsWindow::ConstrainPosition(bool aAllowSlop, int32_t* aX, int32_t* aY) {
2041 ALOG("nsWindow[%p]::ConstrainPosition %d [%d %d]", (void*)this, aAllowSlop,
2042 *aX, *aY);
2044 // constrain toplevel windows; children we don't care about
2045 if (IsTopLevel()) {
2046 *aX = 0;
2047 *aY = 0;
2051 void nsWindow::Move(double aX, double aY) {
2052 if (IsTopLevel()) return;
2054 Resize(aX, aY, mBounds.width, mBounds.height, true);
2057 void nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) {
2058 Resize(mBounds.x, mBounds.y, aWidth, aHeight, aRepaint);
2061 void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
2062 bool aRepaint) {
2063 ALOG("nsWindow[%p]::Resize [%f %f %f %f] (repaint %d)", (void*)this, aX, aY,
2064 aWidth, aHeight, aRepaint);
2066 LayoutDeviceIntRect oldBounds = mBounds;
2068 mBounds.x = NSToIntRound(aX);
2069 mBounds.y = NSToIntRound(aY);
2070 mBounds.width = NSToIntRound(aWidth);
2071 mBounds.height = NSToIntRound(aHeight);
2073 ConstrainSize(&mBounds.width, &mBounds.height);
2075 bool needPositionDispatch = mBounds.TopLeft() != oldBounds.TopLeft();
2076 bool needSizeDispatch = mBounds.Size() != oldBounds.Size();
2078 if (needSizeDispatch) {
2079 OnSizeChanged(mBounds.Size().ToUnknownSize());
2082 if (needPositionDispatch) {
2083 NotifyWindowMoved(mBounds.x, mBounds.y);
2086 // Should we skip honoring aRepaint here?
2087 if (aRepaint && FindTopLevel() == nsWindow::TopWindow()) RedrawAll();
2090 void nsWindow::SetZIndex(int32_t aZIndex) {
2091 ALOG("nsWindow[%p]::SetZIndex %d ignored", (void*)this, aZIndex);
2094 void nsWindow::SetSizeMode(nsSizeMode aMode) {
2095 if (aMode == mSizeMode) {
2096 return;
2099 nsBaseWidget::SetSizeMode(aMode);
2101 switch (aMode) {
2102 case nsSizeMode_Minimized:
2103 java::GeckoAppShell::MoveTaskToBack();
2104 break;
2105 case nsSizeMode_Fullscreen:
2106 MakeFullScreen(true);
2107 break;
2108 default:
2109 break;
2113 void nsWindow::Enable(bool aState) {
2114 ALOG("nsWindow[%p]::Enable %d ignored", (void*)this, aState);
2117 bool nsWindow::IsEnabled() const { return true; }
2119 void nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) {}
2121 nsWindow* nsWindow::FindTopLevel() {
2122 nsWindow* toplevel = this;
2123 while (toplevel) {
2124 if (toplevel->IsTopLevel()) return toplevel;
2126 toplevel = toplevel->mParent;
2129 ALOG(
2130 "nsWindow::FindTopLevel(): couldn't find a toplevel or dialog window in "
2131 "this [%p] widget's hierarchy!",
2132 (void*)this);
2133 return this;
2136 void nsWindow::SetFocus(Raise, mozilla::dom::CallerType aCallerType) {
2137 FindTopLevel()->BringToFront();
2140 void nsWindow::BringToFront() {
2141 MOZ_ASSERT(XRE_IsParentProcess());
2142 // If the window to be raised is the same as the currently raised one,
2143 // do nothing. We need to check the focus manager as well, as the first
2144 // window that is created will be first in the window list but won't yet
2145 // be focused.
2146 nsFocusManager* fm = nsFocusManager::GetFocusManager();
2147 if (fm && fm->GetActiveWindow() && FindTopLevel() == nsWindow::TopWindow()) {
2148 return;
2151 if (!IsTopLevel()) {
2152 FindTopLevel()->BringToFront();
2153 return;
2156 RefPtr<nsWindow> kungFuDeathGrip(this);
2158 nsWindow* oldTop = nullptr;
2159 if (!gTopLevelWindows.IsEmpty()) {
2160 oldTop = gTopLevelWindows[0];
2163 gTopLevelWindows.RemoveElement(this);
2164 gTopLevelWindows.InsertElementAt(0, this);
2166 if (oldTop) {
2167 nsIWidgetListener* listener = oldTop->GetWidgetListener();
2168 if (listener) {
2169 listener->WindowDeactivated();
2173 if (mWidgetListener) {
2174 mWidgetListener->WindowActivated();
2177 RedrawAll();
2180 LayoutDeviceIntRect nsWindow::GetScreenBounds() {
2181 return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size());
2184 LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() {
2185 LayoutDeviceIntPoint p(0, 0);
2187 for (nsWindow* w = this; !!w; w = w->mParent) {
2188 p.x += w->mBounds.x;
2189 p.y += w->mBounds.y;
2191 if (w->IsTopLevel()) {
2192 break;
2195 return p;
2198 nsresult nsWindow::DispatchEvent(WidgetGUIEvent* aEvent,
2199 nsEventStatus& aStatus) {
2200 aStatus = DispatchEvent(aEvent);
2201 return NS_OK;
2204 nsEventStatus nsWindow::DispatchEvent(WidgetGUIEvent* aEvent) {
2205 if (mAttachedWidgetListener) {
2206 return mAttachedWidgetListener->HandleEvent(aEvent, mUseAttachedEvents);
2207 } else if (mWidgetListener) {
2208 return mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents);
2210 return nsEventStatus_eIgnore;
2213 nsresult nsWindow::MakeFullScreen(bool aFullScreen) {
2214 if (!mAndroidView) {
2215 return NS_ERROR_NOT_AVAILABLE;
2218 nsIWidgetListener* listener = GetWidgetListener();
2219 if (listener) {
2220 listener->FullscreenWillChange(aFullScreen);
2223 mIsFullScreen = aFullScreen;
2224 mAndroidView->mEventDispatcher->Dispatch(
2225 aFullScreen ? u"GeckoView:FullScreenEnter" : u"GeckoView:FullScreenExit");
2227 if (listener) {
2228 mSizeMode = mIsFullScreen ? nsSizeMode_Fullscreen : nsSizeMode_Normal;
2229 listener->SizeModeChanged(mSizeMode);
2230 listener->FullscreenChanged(mIsFullScreen);
2232 return NS_OK;
2235 mozilla::WindowRenderer* nsWindow::GetWindowRenderer() {
2236 if (!mWindowRenderer) {
2237 CreateLayerManager();
2240 return mWindowRenderer;
2243 void nsWindow::CreateLayerManager() {
2244 if (mWindowRenderer) {
2245 return;
2248 nsWindow* topLevelWindow = FindTopLevel();
2249 if (!topLevelWindow || topLevelWindow->mWindowType == eWindowType_invisible) {
2250 // don't create a layer manager for an invisible top-level window
2251 return;
2254 // Ensure that gfxPlatform is initialized first.
2255 gfxPlatform::GetPlatform();
2257 if (ShouldUseOffMainThreadCompositing()) {
2258 LayoutDeviceIntRect rect = GetBounds();
2259 CreateCompositor(rect.Width(), rect.Height());
2260 if (mWindowRenderer) {
2261 if (mLayerViewSupport.IsAttached()) {
2262 DispatchToUiThread(
2263 "LayerViewSupport::NotifyCompositorCreated",
2264 [lvs = mLayerViewSupport,
2265 uiCompositorController = GetUiCompositorControllerChild()] {
2266 if (auto lvsAccess{lvs.Access()}) {
2267 lvsAccess->NotifyCompositorCreated(uiCompositorController);
2272 return;
2275 // If we get here, then off main thread compositing failed to initialize.
2276 sFailedToCreateGLContext = true;
2279 if (!ComputeShouldAccelerate() || sFailedToCreateGLContext) {
2280 printf_stderr(" -- creating basic, not accelerated\n");
2281 mWindowRenderer = CreateFallbackRenderer();
2285 void nsWindow::NotifyCompositorSessionLost(
2286 mozilla::layers::CompositorSession* aSession) {
2287 nsBaseWidget::NotifyCompositorSessionLost(aSession);
2289 DispatchToUiThread("nsWindow::NotifyCompositorSessionLost",
2290 [lvs = mLayerViewSupport] {
2291 if (auto lvsAccess{lvs.Access()}) {
2292 lvsAccess->NotifyCompositorSessionLost();
2296 RedrawAll();
2299 void nsWindow::ShowDynamicToolbar() {
2300 auto acc(mGeckoViewSupport.Access());
2301 if (!acc) {
2302 return;
2305 acc->OnShowDynamicToolbar();
2308 void nsWindow::OnSizeChanged(const gfx::IntSize& aSize) {
2309 ALOG("nsWindow: %p OnSizeChanged [%d %d]", (void*)this, aSize.width,
2310 aSize.height);
2312 if (mWidgetListener) {
2313 mWidgetListener->WindowResized(this, aSize.width, aSize.height);
2316 if (mAttachedWidgetListener) {
2317 mAttachedWidgetListener->WindowResized(this, aSize.width, aSize.height);
2321 void nsWindow::InitEvent(WidgetGUIEvent& event, LayoutDeviceIntPoint* aPoint) {
2322 if (aPoint) {
2323 event.mRefPoint = *aPoint;
2324 } else {
2325 event.mRefPoint = LayoutDeviceIntPoint(0, 0);
2328 event.mTime = PR_Now() / 1000;
2331 void nsWindow::UpdateOverscrollVelocity(const float aX, const float aY) {
2332 if (::mozilla::jni::NativeWeakPtr<LayerViewSupport>::Accessor lvs{
2333 mLayerViewSupport.Access()}) {
2334 const auto& compositor = lvs->GetJavaCompositor();
2335 if (AndroidBridge::IsJavaUiThread()) {
2336 compositor->UpdateOverscrollVelocity(aX, aY);
2337 return;
2340 DispatchToUiThread(
2341 "nsWindow::UpdateOverscrollVelocity",
2342 [compositor = GeckoSession::Compositor::GlobalRef(compositor), aX, aY] {
2343 compositor->UpdateOverscrollVelocity(aX, aY);
2348 void nsWindow::UpdateOverscrollOffset(const float aX, const float aY) {
2349 if (::mozilla::jni::NativeWeakPtr<LayerViewSupport>::Accessor lvs{
2350 mLayerViewSupport.Access()}) {
2351 const auto& compositor = lvs->GetJavaCompositor();
2352 if (AndroidBridge::IsJavaUiThread()) {
2353 compositor->UpdateOverscrollOffset(aX, aY);
2354 return;
2357 DispatchToUiThread(
2358 "nsWindow::UpdateOverscrollOffset",
2359 [compositor = GeckoSession::Compositor::GlobalRef(compositor), aX, aY] {
2360 compositor->UpdateOverscrollOffset(aX, aY);
2365 void* nsWindow::GetNativeData(uint32_t aDataType) {
2366 switch (aDataType) {
2367 // used by GLContextProviderEGL, nullptr is EGL_DEFAULT_DISPLAY
2368 case NS_NATIVE_DISPLAY:
2369 return nullptr;
2371 case NS_NATIVE_WIDGET:
2372 return (void*)this;
2374 case NS_RAW_NATIVE_IME_CONTEXT: {
2375 void* pseudoIMEContext = GetPseudoIMEContext();
2376 if (pseudoIMEContext) {
2377 return pseudoIMEContext;
2379 // We assume that there is only one context per process on Android
2380 return NS_ONLY_ONE_NATIVE_IME_CONTEXT;
2383 case NS_JAVA_SURFACE:
2384 if (::mozilla::jni::NativeWeakPtr<LayerViewSupport>::Accessor lvs{
2385 mLayerViewSupport.Access()}) {
2386 return lvs->GetSurface().Get();
2388 return nullptr;
2391 return nullptr;
2394 void nsWindow::SetNativeData(uint32_t aDataType, uintptr_t aVal) {
2395 switch (aDataType) {}
2398 void nsWindow::DispatchHitTest(const WidgetTouchEvent& aEvent) {
2399 if (aEvent.mMessage == eTouchStart && aEvent.mTouches.Length() == 1) {
2400 // Since touch events don't get retargeted by PositionedEventTargeting.cpp
2401 // code, we dispatch a dummy mouse event that *does* get retargeted.
2402 // Front-end code can use this to activate the highlight element in case
2403 // this touchstart is the start of a tap.
2404 WidgetMouseEvent hittest(true, eMouseHitTest, this,
2405 WidgetMouseEvent::eReal);
2406 hittest.mRefPoint = aEvent.mTouches[0]->mRefPoint;
2407 hittest.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
2408 nsEventStatus status;
2409 DispatchEvent(&hittest, status);
2413 void nsWindow::PassExternalResponse(java::WebResponse::Param aResponse) {
2414 auto acc(mGeckoViewSupport.Access());
2415 if (!acc) {
2416 return;
2419 acc->PassExternalResponse(aResponse);
2422 mozilla::Modifiers nsWindow::GetModifiers(int32_t metaState) {
2423 using mozilla::java::sdk::KeyEvent;
2424 return (metaState & KeyEvent::META_ALT_MASK ? MODIFIER_ALT : 0) |
2425 (metaState & KeyEvent::META_SHIFT_MASK ? MODIFIER_SHIFT : 0) |
2426 (metaState & KeyEvent::META_CTRL_MASK ? MODIFIER_CONTROL : 0) |
2427 (metaState & KeyEvent::META_META_MASK ? MODIFIER_META : 0) |
2428 (metaState & KeyEvent::META_FUNCTION_ON ? MODIFIER_FN : 0) |
2429 (metaState & KeyEvent::META_CAPS_LOCK_ON ? MODIFIER_CAPSLOCK : 0) |
2430 (metaState & KeyEvent::META_NUM_LOCK_ON ? MODIFIER_NUMLOCK : 0) |
2431 (metaState & KeyEvent::META_SCROLL_LOCK_ON ? MODIFIER_SCROLLLOCK : 0);
2434 TimeStamp nsWindow::GetEventTimeStamp(int64_t aEventTime) {
2435 // Android's event time is SystemClock.uptimeMillis that is counted in ms
2436 // since OS was booted.
2437 // (https://developer.android.com/reference/android/os/SystemClock.html)
2438 // and this SystemClock.uptimeMillis uses SYSTEM_TIME_MONOTONIC.
2439 // Our posix implemententaion of TimeStamp::Now uses SYSTEM_TIME_MONOTONIC
2440 // too. Due to same implementation, we can use this via FromSystemTime.
2441 int64_t tick =
2442 BaseTimeDurationPlatformUtils::TicksFromMilliseconds(aEventTime);
2443 return TimeStamp::FromSystemTime(tick);
2446 void nsWindow::UserActivity() {
2447 if (!mIdleService) {
2448 mIdleService = do_GetService("@mozilla.org/widget/useridleservice;1");
2451 if (mIdleService) {
2452 mIdleService->ResetIdleTimeOut(0);
2455 if (FindTopLevel() != nsWindow::TopWindow()) {
2456 BringToFront();
2460 RefPtr<mozilla::a11y::SessionAccessibility>
2461 nsWindow::GetSessionAccessibility() {
2462 auto acc(mSessionAccessibility.Access());
2463 if (!acc) {
2464 return nullptr;
2467 return acc.AsRefPtr();
2470 TextEventDispatcherListener* nsWindow::GetNativeTextEventDispatcherListener() {
2471 nsWindow* top = FindTopLevel();
2472 MOZ_ASSERT(top);
2474 auto acc(top->mEditableSupport.Access());
2475 if (!acc) {
2476 // Non-GeckoView windows don't support IME operations.
2477 return nullptr;
2480 nsCOMPtr<TextEventDispatcherListener> ptr;
2481 if (NS_FAILED(acc->QueryInterface(NS_GET_IID(TextEventDispatcherListener),
2482 getter_AddRefs(ptr)))) {
2483 return nullptr;
2486 return ptr.get();
2489 void nsWindow::SetInputContext(const InputContext& aContext,
2490 const InputContextAction& aAction) {
2491 nsWindow* top = FindTopLevel();
2492 MOZ_ASSERT(top);
2494 auto acc(top->mEditableSupport.Access());
2495 if (!acc) {
2496 // Non-GeckoView windows don't support IME operations.
2497 return;
2500 // We are using an IME event later to notify Java, and the IME event
2501 // will be processed by the top window. Therefore, to ensure the
2502 // IME event uses the correct mInputContext, we need to let the top
2503 // window process SetInputContext
2504 acc->SetInputContext(aContext, aAction);
2507 InputContext nsWindow::GetInputContext() {
2508 nsWindow* top = FindTopLevel();
2509 MOZ_ASSERT(top);
2511 auto acc(top->mEditableSupport.Access());
2512 if (!acc) {
2513 // Non-GeckoView windows don't support IME operations.
2514 return InputContext();
2517 // We let the top window process SetInputContext,
2518 // so we should let it process GetInputContext as well.
2519 return acc->GetInputContext();
2522 nsresult nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
2523 TouchPointerState aPointerState,
2524 LayoutDeviceIntPoint aPoint,
2525 double aPointerPressure,
2526 uint32_t aPointerOrientation,
2527 nsIObserver* aObserver) {
2528 mozilla::widget::AutoObserverNotifier notifier(aObserver, "touchpoint");
2530 int eventType;
2531 switch (aPointerState) {
2532 case TOUCH_CONTACT:
2533 // This could be a ACTION_DOWN or ACTION_MOVE depending on the
2534 // existing state; it is mapped to the right thing in Java.
2535 eventType = java::sdk::MotionEvent::ACTION_POINTER_DOWN;
2536 break;
2537 case TOUCH_REMOVE:
2538 // This could be turned into a ACTION_UP in Java
2539 eventType = java::sdk::MotionEvent::ACTION_POINTER_UP;
2540 break;
2541 case TOUCH_CANCEL:
2542 eventType = java::sdk::MotionEvent::ACTION_CANCEL;
2543 break;
2544 case TOUCH_HOVER: // not supported for now
2545 default:
2546 return NS_ERROR_UNEXPECTED;
2549 MOZ_ASSERT(mNPZCSupport.IsAttached());
2550 auto npzcSup(mNPZCSupport.Access());
2551 MOZ_ASSERT(!!npzcSup);
2553 const auto& npzc = npzcSup->GetJavaNPZC();
2554 const auto& bounds = FindTopLevel()->mBounds;
2555 aPoint.x -= bounds.x;
2556 aPoint.y -= bounds.y;
2558 DispatchToUiThread(
2559 "nsWindow::SynthesizeNativeTouchPoint",
2560 [npzc = java::PanZoomController::NativeProvider::GlobalRef(npzc),
2561 aPointerId, eventType, aPoint, aPointerPressure, aPointerOrientation] {
2562 npzc->SynthesizeNativeTouchPoint(aPointerId, eventType, aPoint.x,
2563 aPoint.y, aPointerPressure,
2564 aPointerOrientation);
2566 return NS_OK;
2569 nsresult nsWindow::SynthesizeNativeMouseEvent(
2570 LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage,
2571 MouseButton aButton, nsIWidget::Modifiers aModifierFlags,
2572 nsIObserver* aObserver) {
2573 mozilla::widget::AutoObserverNotifier notifier(aObserver, "mouseevent");
2575 MOZ_ASSERT(mNPZCSupport.IsAttached());
2576 auto npzcSup(mNPZCSupport.Access());
2577 MOZ_ASSERT(!!npzcSup);
2579 const auto& npzc = npzcSup->GetJavaNPZC();
2580 const auto& bounds = FindTopLevel()->mBounds;
2581 aPoint.x -= bounds.x;
2582 aPoint.y -= bounds.y;
2584 int32_t nativeMessage;
2585 switch (aNativeMessage) {
2586 case NativeMouseMessage::ButtonDown:
2587 nativeMessage = java::sdk::MotionEvent::ACTION_POINTER_DOWN;
2588 break;
2589 case NativeMouseMessage::ButtonUp:
2590 nativeMessage = java::sdk::MotionEvent::ACTION_POINTER_UP;
2591 break;
2592 case NativeMouseMessage::Move:
2593 nativeMessage = java::sdk::MotionEvent::ACTION_HOVER_MOVE;
2594 break;
2595 default:
2596 MOZ_ASSERT_UNREACHABLE("Non supported mouse event on Android");
2597 return NS_ERROR_INVALID_ARG;
2599 int32_t button = 0;
2600 if (aNativeMessage != NativeMouseMessage::ButtonUp) {
2601 switch (aButton) {
2602 case MouseButton::ePrimary:
2603 button = java::sdk::MotionEvent::BUTTON_PRIMARY;
2604 break;
2605 case MouseButton::eMiddle:
2606 button = java::sdk::MotionEvent::BUTTON_TERTIARY;
2607 break;
2608 case MouseButton::eSecondary:
2609 button = java::sdk::MotionEvent::BUTTON_SECONDARY;
2610 break;
2611 case MouseButton::eX1:
2612 button = java::sdk::MotionEvent::BUTTON_BACK;
2613 break;
2614 case MouseButton::eX2:
2615 button = java::sdk::MotionEvent::BUTTON_FORWARD;
2616 break;
2617 default:
2618 if (aNativeMessage == NativeMouseMessage::ButtonDown) {
2619 MOZ_ASSERT_UNREACHABLE("Non supported mouse button type on Android");
2620 return NS_ERROR_INVALID_ARG;
2622 break;
2626 // TODO (bug 1693237): Handle aModifierFlags.
2627 DispatchToUiThread(
2628 "nsWindow::SynthesizeNativeMouseEvent",
2629 [npzc = java::PanZoomController::NativeProvider::GlobalRef(npzc),
2630 nativeMessage, aPoint, button] {
2631 npzc->SynthesizeNativeMouseEvent(nativeMessage, aPoint.x, aPoint.y,
2632 button);
2634 return NS_OK;
2637 nsresult nsWindow::SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,
2638 nsIObserver* aObserver) {
2639 return SynthesizeNativeMouseEvent(
2640 aPoint, NativeMouseMessage::Move, MouseButton::eNotPressed,
2641 nsIWidget::Modifiers::NO_MODIFIERS, aObserver);
2644 void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) {
2645 if (delegate) {
2646 mCompositorWidgetDelegate = delegate->AsPlatformSpecificDelegate();
2647 MOZ_ASSERT(mCompositorWidgetDelegate,
2648 "nsWindow::SetCompositorWidgetDelegate called with a "
2649 "non-PlatformCompositorWidgetDelegate");
2650 } else {
2651 mCompositorWidgetDelegate = nullptr;
2655 void nsWindow::GetCompositorWidgetInitData(
2656 mozilla::widget::CompositorWidgetInitData* aInitData) {
2657 *aInitData = mozilla::widget::AndroidCompositorWidgetInitData(
2658 mWidgetId, GetClientSize());
2661 bool nsWindow::WidgetPaintsBackground() {
2662 return StaticPrefs::android_widget_paints_background();
2665 bool nsWindow::NeedsPaint() {
2666 auto lvs(mLayerViewSupport.Access());
2667 if (!lvs || lvs->CompositorPaused() || !GetWindowRenderer()) {
2668 return false;
2671 return nsIWidget::NeedsPaint();
2674 void nsWindow::ConfigureAPZControllerThread() {
2675 nsCOMPtr<nsISerialEventTarget> thread = mozilla::GetAndroidUiThread();
2676 APZThreadUtils::SetControllerThread(thread);
2679 already_AddRefed<GeckoContentController>
2680 nsWindow::CreateRootContentController() {
2681 RefPtr<GeckoContentController> controller =
2682 new AndroidContentController(this, mAPZEventState, mAPZC);
2683 return controller.forget();
2686 uint32_t nsWindow::GetMaxTouchPoints() const {
2687 return java::GeckoAppShell::GetMaxTouchPoints();
2690 void nsWindow::UpdateZoomConstraints(
2691 const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId,
2692 const mozilla::Maybe<ZoomConstraints>& aConstraints) {
2693 nsBaseWidget::UpdateZoomConstraints(aPresShellId, aViewId, aConstraints);
2696 CompositorBridgeChild* nsWindow::GetCompositorBridgeChild() const {
2697 return mCompositorSession ? mCompositorSession->GetCompositorBridgeChild()
2698 : nullptr;
2701 void nsWindow::SetContentDocumentDisplayed(bool aDisplayed) {
2702 mContentDocumentDisplayed = aDisplayed;
2705 bool nsWindow::IsContentDocumentDisplayed() {
2706 return mContentDocumentDisplayed;
2709 void nsWindow::RecvToolbarAnimatorMessageFromCompositor(int32_t aMessage) {
2710 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
2711 if (::mozilla::jni::NativeWeakPtr<LayerViewSupport>::Accessor lvs{
2712 mLayerViewSupport.Access()}) {
2713 lvs->RecvToolbarAnimatorMessage(aMessage);
2717 void nsWindow::UpdateRootFrameMetrics(const ScreenPoint& aScrollOffset,
2718 const CSSToScreenScale& aZoom) {
2719 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
2720 if (::mozilla::jni::NativeWeakPtr<LayerViewSupport>::Accessor lvs{
2721 mLayerViewSupport.Access()}) {
2722 const auto& compositor = lvs->GetJavaCompositor();
2723 mContentDocumentDisplayed = true;
2724 compositor->UpdateRootFrameMetrics(aScrollOffset.x, aScrollOffset.y,
2725 aZoom.scale);
2729 void nsWindow::RecvScreenPixels(Shmem&& aMem, const ScreenIntSize& aSize,
2730 bool aNeedsYFlip) {
2731 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
2732 if (::mozilla::jni::NativeWeakPtr<LayerViewSupport>::Accessor lvs{
2733 mLayerViewSupport.Access()}) {
2734 lvs->RecvScreenPixels(std::move(aMem), aSize, aNeedsYFlip);
2738 void nsWindow::UpdateDynamicToolbarMaxHeight(ScreenIntCoord aHeight) {
2739 if (mDynamicToolbarMaxHeight == aHeight) {
2740 return;
2743 mDynamicToolbarMaxHeight = aHeight;
2745 if (mWidgetListener) {
2746 mWidgetListener->DynamicToolbarMaxHeightChanged(aHeight);
2749 if (mAttachedWidgetListener) {
2750 mAttachedWidgetListener->DynamicToolbarMaxHeightChanged(aHeight);
2754 void nsWindow::UpdateDynamicToolbarOffset(ScreenIntCoord aOffset) {
2755 if (mWidgetListener) {
2756 mWidgetListener->DynamicToolbarOffsetChanged(aOffset);
2759 if (mAttachedWidgetListener) {
2760 mAttachedWidgetListener->DynamicToolbarOffsetChanged(aOffset);
2764 ScreenIntMargin nsWindow::GetSafeAreaInsets() const { return mSafeAreaInsets; }
2766 void nsWindow::UpdateSafeAreaInsets(const ScreenIntMargin& aSafeAreaInsets) {
2767 mSafeAreaInsets = aSafeAreaInsets;
2769 if (mWidgetListener) {
2770 mWidgetListener->SafeAreaInsetsChanged(aSafeAreaInsets);
2773 if (mAttachedWidgetListener) {
2774 mAttachedWidgetListener->SafeAreaInsetsChanged(aSafeAreaInsets);
2778 already_AddRefed<nsIWidget> nsIWidget::CreateTopLevelWindow() {
2779 nsCOMPtr<nsIWidget> window = new nsWindow();
2780 return window.forget();
2783 already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() {
2784 nsCOMPtr<nsIWidget> window = new nsWindow();
2785 return window.forget();
2788 static already_AddRefed<DataSourceSurface> GetCursorImage(
2789 const nsIWidget::Cursor& aCursor, mozilla::CSSToLayoutDeviceScale aScale) {
2790 if (!aCursor.IsCustom()) {
2791 return nullptr;
2794 RefPtr<DataSourceSurface> destDataSurface;
2796 nsIntSize size = nsIWidget::CustomCursorSize(aCursor);
2797 // prevent DoS attacks
2798 if (size.width > 128 || size.height > 128) {
2799 return nullptr;
2802 RefPtr<gfx::SourceSurface> surface = aCursor.mContainer->GetFrameAtSize(
2803 size * aScale.scale, imgIContainer::FRAME_CURRENT,
2804 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
2805 if (NS_WARN_IF(!surface)) {
2806 return nullptr;
2809 RefPtr<DataSourceSurface> srcDataSurface = surface->GetDataSurface();
2810 if (NS_WARN_IF(!srcDataSurface)) {
2811 return nullptr;
2814 DataSourceSurface::ScopedMap sourceMap(srcDataSurface,
2815 DataSourceSurface::READ);
2817 destDataSurface = gfx::Factory::CreateDataSourceSurfaceWithStride(
2818 srcDataSurface->GetSize(), SurfaceFormat::R8G8B8A8,
2819 sourceMap.GetStride());
2820 if (NS_WARN_IF(!destDataSurface)) {
2821 return nullptr;
2824 DataSourceSurface::ScopedMap destMap(destDataSurface,
2825 DataSourceSurface::READ_WRITE);
2827 SwizzleData(sourceMap.GetData(), sourceMap.GetStride(), surface->GetFormat(),
2828 destMap.GetData(), destMap.GetStride(), SurfaceFormat::R8G8B8A8,
2829 destDataSurface->GetSize());
2831 return destDataSurface.forget();
2834 static int32_t GetCursorType(nsCursor aCursor) {
2835 // When our minimal requirement of SDK version is 25+,
2836 // we should replace with JNI auto-generator.
2837 switch (aCursor) {
2838 case eCursor_standard:
2839 // android.view.PointerIcon.TYPE_ARROW
2840 return 0x3e8;
2841 case eCursor_wait:
2842 // android.view.PointerIcon.TYPE_WAIT
2843 return 0x3ec;
2844 case eCursor_select:
2845 // android.view.PointerIcon.TYPE_TEXT;
2846 return 0x3f0;
2847 case eCursor_hyperlink:
2848 // android.view.PointerIcon.TYPE_HAND
2849 return 0x3ea;
2850 case eCursor_n_resize:
2851 case eCursor_s_resize:
2852 case eCursor_ns_resize:
2853 case eCursor_row_resize:
2854 // android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW
2855 return 0x3f7;
2856 case eCursor_w_resize:
2857 case eCursor_e_resize:
2858 case eCursor_ew_resize:
2859 case eCursor_col_resize:
2860 // android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW
2861 return 0x3f6;
2862 case eCursor_nw_resize:
2863 case eCursor_se_resize:
2864 case eCursor_nwse_resize:
2865 // android.view.PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW
2866 return 0x3f9;
2867 case eCursor_ne_resize:
2868 case eCursor_sw_resize:
2869 case eCursor_nesw_resize:
2870 // android.view.PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW
2871 return 0x3f8;
2872 case eCursor_crosshair:
2873 // android.view.PointerIcon.TYPE_CROSSHAIR
2874 return 0x3ef;
2875 case eCursor_move:
2876 // android.view.PointerIcon.TYPE_ARROW
2877 return 0x3e8;
2878 case eCursor_help:
2879 // android.view.PointerIcon.TYPE_HELP
2880 return 0x3eb;
2881 case eCursor_copy:
2882 // android.view.PointerIcon.TYPE_COPY
2883 return 0x3f3;
2884 case eCursor_alias:
2885 // android.view.PointerIcon.TYPE_ALIAS
2886 return 0x3f2;
2887 case eCursor_context_menu:
2888 // android.view.PointerIcon.TYPE_CONTEXT_MENU
2889 return 0x3e9;
2890 case eCursor_cell:
2891 // android.view.PointerIcon.TYPE_CELL
2892 return 0x3ee;
2893 case eCursor_grab:
2894 // android.view.PointerIcon.TYPE_GRAB
2895 return 0x3fc;
2896 case eCursor_grabbing:
2897 // android.view.PointerIcon.TYPE_GRABBING
2898 return 0x3fd;
2899 case eCursor_spinning:
2900 // android.view.PointerIcon.TYPE_WAIT
2901 return 0x3ec;
2902 case eCursor_zoom_in:
2903 // android.view.PointerIcon.TYPE_ZOOM_IN
2904 return 0x3fa;
2905 case eCursor_zoom_out:
2906 // android.view.PointerIcon.TYPE_ZOOM_OUT
2907 return 0x3fb;
2908 case eCursor_not_allowed:
2909 // android.view.PointerIcon.TYPE_NO_DROP:
2910 return 0x3f4;
2911 case eCursor_no_drop:
2912 // android.view.PointerIcon.TYPE_NO_DROP:
2913 return 0x3f4;
2914 case eCursor_vertical_text:
2915 // android.view.PointerIcon.TYPE_VERTICAL_TEXT
2916 return 0x3f1;
2917 case eCursor_all_scroll:
2918 // android.view.PointerIcon.TYPE_ALL_SCROLL
2919 return 0x3f5;
2920 case eCursor_none:
2921 // android.view.PointerIcon.TYPE_NULL
2922 return 0;
2923 default:
2924 NS_WARNING_ASSERTION(aCursor, "Invalid cursor type");
2925 // android.view.PointerIcon.TYPE_ARROW
2926 return 0x3e8;
2930 void nsWindow::SetCursor(const Cursor& aCursor) {
2931 if (mozilla::jni::GetAPIVersion() < 24) {
2932 return;
2935 // Only change cursor if it's actually been changed
2936 if (!mUpdateCursor && mCursor == aCursor) {
2937 return;
2940 mUpdateCursor = false;
2941 mCursor = aCursor;
2943 int32_t type = 0;
2944 RefPtr<DataSourceSurface> destDataSurface =
2945 GetCursorImage(aCursor, GetDefaultScale());
2946 if (!destDataSurface) {
2947 type = GetCursorType(aCursor.mDefaultCursor);
2950 if (mozilla::jni::NativeWeakPtr<LayerViewSupport>::Accessor lvs{
2951 mLayerViewSupport.Access()}) {
2952 const auto& compositor = lvs->GetJavaCompositor();
2954 DispatchToUiThread(
2955 "nsWindow::SetCursor",
2956 [compositor = GeckoSession::Compositor::GlobalRef(compositor), type,
2957 destDataSurface = std::move(destDataSurface),
2958 hotspotX = aCursor.mHotspotX, hotspotY = aCursor.mHotspotY] {
2959 java::sdk::Bitmap::LocalRef bitmap;
2960 if (destDataSurface) {
2961 DataSourceSurface::ScopedMap destMap(destDataSurface,
2962 DataSourceSurface::READ);
2963 auto pixels = mozilla::jni::ByteBuffer::New(
2964 reinterpret_cast<int8_t*>(destMap.GetData()),
2965 destMap.GetStride() * destDataSurface->GetSize().height);
2966 bitmap = java::sdk::Bitmap::CreateBitmap(
2967 destDataSurface->GetSize().width,
2968 destDataSurface->GetSize().height,
2969 java::sdk::Config::ARGB_8888());
2970 bitmap->CopyPixelsFromBuffer(pixels);
2972 compositor->SetPointerIcon(type, bitmap, hotspotX, hotspotY);