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/. */
9 #include <android/log.h>
10 #include <android/native_window.h>
11 #include <android/native_window_jni.h>
14 #include <type_traits>
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"
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"
50 #include "nsThreadUtils.h"
51 #include "nsUserIdleService.h"
52 #include "nsViewManager.h"
53 #include "nsWidgetsCID.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
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};
142 template <class Instance
, class Impl
>
143 std::enable_if_t
<jni::detail::NativePtrPicker
<Impl
>::value
==
144 jni::detail::NativePtrType::REFPTR
,
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
,
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
)));
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
{
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
;
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();
217 auto acc
= npzcSupportWeak
->Access();
219 // We already shut down.
220 env
->ExceptionClear();
224 auto win
= acc
->mWindow
.Access();
226 // We already shut down.
227 env
->ExceptionClear();
231 nsWindow
* const window
= win
->GetNsWindow();
233 // We already shut down.
234 env
->ExceptionClear();
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
)));
253 typedef java::PanZoomController::NativeProvider::Natives
<NPZCSupport
> Base
;
255 NPZCSupport(WindowPtr aWindow
,
256 const java::PanZoomController::NativeProvider::LocalRef
& aNPZC
)
257 : mWindow(aWindow
), mNPZC(aNPZC
) {
259 auto win(mWindow
.Access());
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();
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
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
);
321 NS_NewRunnableFunction("NPZCSupport::OnWeakNonIntrusiveDetach",
322 [npzc
, disposer
= std::move(disposer
)] {
323 npzc
->SetAttached(false);
329 const java::PanZoomController::NativeProvider::Ref
& GetJavaNPZC() const {
334 void SetIsLongpressEnabled(bool aIsLongpressEnabled
) {
335 RefPtr
<IAPZCTreeManager
> controller
;
337 if (auto window
= mWindow
.Access()) {
338 nsWindow
* gkWindow
= window
->GetNsWindow();
340 controller
= gkWindow
->mAPZC
;
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();
358 controller
= gkWindow
->mAPZC
;
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
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
;
402 MOZ_ASSERT_UNREACHABLE("Unexpected nsEventStatus");
403 return INPUT_RESULT_UNHANDLED
;
408 static MouseInput::ButtonType
GetButtonType(int button
) {
409 MouseInput::ButtonType result
= MouseInput::NONE
;
412 case java::sdk::MotionEvent::BUTTON_PRIMARY
:
413 result
= MouseInput::PRIMARY_BUTTON
;
415 case java::sdk::MotionEvent::BUTTON_SECONDARY
:
416 result
= MouseInput::SECONDARY_BUTTON
;
418 case java::sdk::MotionEvent::BUTTON_TERTIARY
:
419 result
= MouseInput::MIDDLE_BUTTON
;
428 static int16_t ConvertButtons(int buttons
) {
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
;
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
;
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
;
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
));
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();
513 controller
= gkWindow
->mAPZC
;
518 return INPUT_RESULT_UNHANDLED
;
521 MouseInput::MouseType mouseType
= MouseInput::MOUSE_NONE
;
522 MouseInput::ButtonType buttonType
= MouseInput::NONE
;
524 case java::sdk::MotionEvent::ACTION_DOWN
:
525 mouseType
= MouseInput::MOUSE_DOWN
;
526 buttonType
= GetButtonType(buttons
^ mPreviousButtons
);
527 mPreviousButtons
= buttons
;
529 case java::sdk::MotionEvent::ACTION_UP
:
530 mouseType
= MouseInput::MOUSE_UP
;
531 buttonType
= GetButtonType(buttons
^ mPreviousButtons
);
532 mPreviousButtons
= buttons
;
534 case java::sdk::MotionEvent::ACTION_MOVE
:
535 mouseType
= MouseInput::MOUSE_MOVE
;
537 case java::sdk::MotionEvent::ACTION_HOVER_MOVE
:
538 mouseType
= MouseInput::MOUSE_MOVE
;
540 case java::sdk::MotionEvent::ACTION_HOVER_ENTER
:
541 mouseType
= MouseInput::MOUSE_WIDGET_ENTER
;
543 case java::sdk::MotionEvent::ACTION_HOVER_EXIT
:
544 mouseType
= MouseInput::MOUSE_WIDGET_EXIT
;
550 if (mouseType
== MouseInput::MOUSE_NONE
) {
551 return INPUT_RESULT_UNHANDLED
;
554 ScreenPoint origin
= ScreenPoint(aX
, aY
);
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
;
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
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.
608 ScreenSize(int32_t(aToolMajor
/ 2.0f
), int32_t(aToolMinor
/ 2.0f
));
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
);
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
;
637 case java::sdk::MotionEvent::ACTION_MOVE
:
638 type
= MultiTouchInput::MULTITOUCH_MOVE
;
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;
648 case java::sdk::MotionEvent::ACTION_OUTSIDE
:
649 case java::sdk::MotionEvent::ACTION_CANCEL
:
650 type
= MultiTouchInput::MULTITOUCH_CANCEL
;
654 returnResult
->Complete(
655 java::sdk::Integer::ValueOf(INPUT_RESULT_UNHANDLED
));
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
,
715 for (size_t historyIndex
= 0; historyIndex
< historySize
;
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
]),
728 {}, // mLocalScreenPoint will be computed later by APZ
731 historicalPressure
[historicalI
]});
734 input
.mTouches
.AppendElement(singleTouchData
);
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
));
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());
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();
802 controller
= gkWindow
->mAPZC
;
808 aReturnResult
->Complete(java::PanZoomController::InputResultDetail::New(
809 INPUT_RESULT_UNHANDLED
,
810 java::PanZoomController::SCROLLABLE_FLAG_NONE
,
811 java::PanZoomController::OVERSCROLL_FLAG_NONE
));
816 APZInputBridge::InputBlockCallback callback
;
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
) {
829 aReturnResult
->Complete(java::PanZoomController::InputResultDetail::New(
830 INPUT_RESULT_IGNORED
, java::PanZoomController::SCROLLABLE_FLAG_NONE
,
831 java::PanZoomController::OVERSCROLL_FLAG_NONE
));
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
));
854 case nsEventStatus_eConsumeDoDefault
:
855 aReturnResult
->Complete(
856 ConvertAPZHandledResult(result
.GetHandledResult().value()));
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
));
871 NS_IMPL_ISUPPORTS(AndroidView
, nsIAndroidEventDispatcher
, nsIAndroidView
)
873 nsresult
AndroidView::GetInitData(JSContext
* aCx
, JS::MutableHandleValue aOut
) {
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
> {
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
)
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
;
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
;
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();
943 event
->setPrevious(this);
945 queue
.insertBack(this);
951 typedef GeckoSession::Compositor::Natives
<LayerViewSupport
> Base
;
953 LayerViewSupport(WindowPtr aWindow
,
954 const GeckoSession::Compositor::LocalRef
& aInstance
)
955 : mWindow(aWindow
), mCompositor(aInstance
), mCompositorPaused(true) {
957 auto win(mWindow
.Access());
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
);
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()) {
982 java::GeckoResult::LocalRef(results
->front().mResult
);
984 aResult
->CompleteExceptionally(
985 java::sdk::IllegalStateException::New(
986 "The compositor has detached from the session")
987 .Cast
<jni::Throwable
>());
993 compositor
->OnCompositorDetached();
999 const GeckoSession::Compositor::Ref
& GetJavaCompositor() const {
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();
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()) {
1037 java::GeckoResult::LocalRef(mCapturePixelsResults
.front().mResult
);
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
; }
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
);
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
1085 void AttachNPZC(jni::Object::Param aNPZC
) {
1086 MOZ_ASSERT(NS_IsMainThread());
1089 auto locked(mWindow
.Access());
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
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
);
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
,
1117 MOZ_ASSERT(NS_IsMainThread());
1118 auto acc
= mWindow
.Access();
1120 return; // Already shut down.
1123 nsWindow
* gkWindow
= acc
->GetNsWindow();
1128 gkWindow
->Resize(aLeft
, aTop
, aWidth
, aHeight
, /* repaint */ false);
1131 void NotifyMemoryPressure() {
1132 MOZ_ASSERT(NS_IsMainThread());
1133 auto acc
= mWindow
.Access();
1135 return; // Already shut down.
1138 nsWindow
* gkWindow
= acc
->GetNsWindow();
1139 if (!gkWindow
|| !gkWindow
->mCompositorBridgeChild
) {
1143 gkWindow
->mCompositorBridgeChild
->SendNotifyMemoryPressure();
1146 void SetDynamicToolbarMaxHeight(int32_t aHeight
) {
1147 MOZ_ASSERT(NS_IsMainThread());
1148 auto acc
= mWindow
.Access();
1150 return; // Already shut down.
1153 nsWindow
* gkWindow
= acc
->GetNsWindow();
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()) {
1173 java::GeckoResult::LocalRef(mCapturePixelsResults
.front().mResult
);
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();
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
;
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
));
1231 env
->ExceptionClear();
1232 return; // Already shut down.
1235 auto lvs(lvsHolder
->Access());
1237 env
->ExceptionClear();
1238 return; // Already shut down.
1241 auto win
= lvs
->mWindow
.Access();
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();
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
) {
1271 if (AndroidBridge::IsJavaUiThread()) {
1272 mUiCompositorControllerChild
->InvalidateAndRender();
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();
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
);
1311 void SendToolbarAnimatorMessage(int32_t aMessage
) {
1312 if (!mUiCompositorControllerChild
) {
1316 if (AndroidBridge::IsJavaUiThread()) {
1317 mUiCompositorControllerChild
->ToolbarAnimatorMessageFromUI(aMessage
);
1321 if (RefPtr
<nsThread
> uiThread
= GetAndroidUiThread()) {
1323 NewRunnableMethod
<int32_t>(
1324 "LayerViewSupport::ToolbarAnimatorMessageFromUI",
1325 mUiCompositorControllerChild
,
1326 &UiCompositorControllerChild::ToolbarAnimatorMessageFromUI
,
1328 nsIThread::DISPATCH_NORMAL
);
1332 void RecvToolbarAnimatorMessage(int32_t aMessage
) {
1333 auto compositor
= GeckoSession::Compositor::LocalRef(mCompositor
);
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
) {
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
>());
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();
1378 mUiCompositorControllerChild
->RequestScreenPixels();
1382 void RecvScreenPixels(Shmem
&& aMem
, const ScreenIntSize
& aSize
,
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();
1401 RefPtr
<DataSourceSurface
> surf
;
1403 surf
= FlipScreenPixels(aMem
, aSize
, request
.mSource
,
1404 request
.mOutputSize
);
1406 surf
= gfx::Factory::CreateWrappingDataSourceSurface(
1407 aMem
.get
<uint8_t>(),
1408 StrideForFormatAndWidth(SurfaceFormat::B8G8R8A8
, aSize
.width
),
1409 IntSize(aSize
.width
, aSize
.height
), SurfaceFormat::B8G8R8A8
);
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
);
1419 result
->CompleteExceptionally(
1420 java::sdk::IllegalStateException::New(
1421 "Failed to create flipped snapshot surface (probably out of "
1423 .Cast
<jni::Throwable
>());
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
,
1453 MOZ_ASSERT(NS_IsMainThread());
1454 auto win(mWindow
.Access());
1456 return; // Already shut down.
1459 nsWindow
* gkWindow
= win
->GetNsWindow();
1464 ScreenIntMargin
safeAreaInsets(aTop
, aRight
, aBottom
, aLeft
);
1465 gkWindow
->UpdateSafeAreaInsets(safeAreaInsets
);
1469 GeckoViewSupport::~GeckoViewSupport() {
1471 mWindow
->DetachNatives();
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
1489 gfxPlatform::GetPlatform();
1491 nsCOMPtr
<nsIWindowWatcher
> ww
= do_GetService(NS_WINDOWWATCHER_CONTRACTID
);
1492 MOZ_RELEASE_ASSERT(ww
);
1496 url
= aChromeURI
->ToCString();
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");
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
);
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());
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() {
1553 if (mWindow
->mAndroidView
) {
1554 mWindow
->mAndroidView
->mEventDispatcher
->Detach();
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
));
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
;
1588 mWindow
->mLayerViewSupport
=
1589 jni::NativeWeakPtrHolder
<LayerViewSupport
>::Attach(
1590 compositor
, mWindow
->mGeckoViewSupport
, compositor
);
1592 if (RefPtr
<UiCompositorControllerChild
> uiCompositorController
=
1593 mWindow
->GetUiCompositorControllerChild()) {
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
);
1614 // We're in a transfer; update init-data and notify JS code.
1615 mWindow
->mAndroidView
->mInitData
= java::GeckoBundle::Ref::From(aInitData
);
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
);
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(
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
);
1666 return window
->OnLoadRequest(aUri
, aWindowType
, aFlags
, aTriggeringUri
,
1667 aHasUserGesture
, aIsTopLevel
);
1670 void GeckoViewSupport::OnShowDynamicToolbar() const {
1671 GeckoSession::Window::LocalRef
window(mGeckoViewWindow
);
1676 window
->OnShowDynamicToolbar();
1679 void GeckoViewSupport::OnReady(jni::Object::Param aQueue
) {
1680 GeckoSession::Window::LocalRef
window(mGeckoViewWindow
);
1684 window
->OnReady(aQueue
);
1688 void GeckoViewSupport::PassExternalResponse(
1689 java::WebResponse::Param aResponse
) {
1690 GeckoSession::Window::LocalRef
window(mGeckoViewWindow
);
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();
1724 already_AddRefed
<nsWindow
> nsWindow::From(nsPIDOMWindowOuter
* aDOMWindow
) {
1725 nsCOMPtr
<nsIWidget
> widget
= WidgetUtils::DOMWindowToWidget(aDOMWindow
);
1726 return From(widget
);
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
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();
1745 nsWindow
* nsWindow::TopWindow() {
1746 if (!gTopLevelWindows
.IsEmpty()) return gTopLevelWindows
[0];
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
,
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
),
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
) {
1804 "Ignoring native parent on Android window [%p], "
1805 "since parent was specified (%p %p)",
1806 (void*)this, (void*)aNativeParent
, (void*)aParent
);
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) {
1823 SetSizeConstraints(SizeConstraints());
1825 BaseCreate(nullptr, aInitData
);
1827 NS_ASSERTION(IsTopLevel() || parent
,
1828 "non-top-level window doesn't have a parent!");
1831 gTopLevelWindows
.AppendElement(this);
1833 } else if (parent
) {
1834 parent
->mChildren
.AppendElement(this);
1838 #ifdef DEBUG_ANDROID_WIDGET
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
1863 nsBaseWidget::DestroyCompositor();
1865 nsBaseWidget::Destroy();
1867 if (IsTopLevel()) gTopLevelWindows
.RemoveElement(this);
1871 nsBaseWidget::OnDestroy();
1873 #ifdef DEBUG_ANDROID_WIDGET
1878 mozilla::widget::EventDispatcher
* nsWindow::GetEventDispatcher() const {
1880 return mAndroidView
->mEventDispatcher
;
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()
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());
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
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
,
1934 auto geckoViewSupport(mGeckoViewSupport
.Access());
1935 if (!geckoViewSupport
) {
1936 return MozPromise
<bool, bool, false>::CreateAndResolve(false, __func__
);
1939 nsAutoCString spec
, triggeringSpec
;
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
,
1967 ? MozPromise
<bool, bool, false>::FromGeckoResult(geckoResult
)
1971 float nsWindow::GetDPI() {
1974 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
1976 screen
->GetDpi(&dpi
);
1982 double nsWindow::GetDefaultScaleInternal() {
1983 double scale
= 1.0f
;
1985 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
1987 screen
->GetContentsScaleFactor(&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..");
2001 if (aState
== mIsVisible
) return;
2003 mIsVisible
= aState
;
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.
2015 // It just became visible, so bring it to the front.
2018 } else if (nsWindow::TopWindow() == this) {
2019 // find the next visible window to show
2021 for (i
= 1; i
< gTopLevelWindows
.Length(); i
++) {
2022 nsWindow
* win
= gTopLevelWindows
[i
];
2023 if (!win
->mIsVisible
) continue;
2025 win
->BringToFront();
2029 } else if (FindTopLevel() == nsWindow::TopWindow()) {
2033 #ifdef DEBUG_ANDROID_WIDGET
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
,
2044 // constrain toplevel windows; children we don't care about
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
,
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
) {
2099 nsBaseWidget::SetSizeMode(aMode
);
2102 case nsSizeMode_Minimized
:
2103 java::GeckoAppShell::MoveTaskToBack();
2105 case nsSizeMode_Fullscreen
:
2106 MakeFullScreen(true);
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;
2124 if (toplevel
->IsTopLevel()) return toplevel
;
2126 toplevel
= toplevel
->mParent
;
2130 "nsWindow::FindTopLevel(): couldn't find a toplevel or dialog window in "
2131 "this [%p] widget's hierarchy!",
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
2146 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
2147 if (fm
&& fm
->GetActiveWindow() && FindTopLevel() == nsWindow::TopWindow()) {
2151 if (!IsTopLevel()) {
2152 FindTopLevel()->BringToFront();
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);
2167 nsIWidgetListener
* listener
= oldTop
->GetWidgetListener();
2169 listener
->WindowDeactivated();
2173 if (mWidgetListener
) {
2174 mWidgetListener
->WindowActivated();
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()) {
2198 nsresult
nsWindow::DispatchEvent(WidgetGUIEvent
* aEvent
,
2199 nsEventStatus
& aStatus
) {
2200 aStatus
= DispatchEvent(aEvent
);
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();
2220 listener
->FullscreenWillChange(aFullScreen
);
2223 mIsFullScreen
= aFullScreen
;
2224 mAndroidView
->mEventDispatcher
->Dispatch(
2225 aFullScreen
? u
"GeckoView:FullScreenEnter" : u
"GeckoView:FullScreenExit");
2228 mSizeMode
= mIsFullScreen
? nsSizeMode_Fullscreen
: nsSizeMode_Normal
;
2229 listener
->SizeModeChanged(mSizeMode
);
2230 listener
->FullscreenChanged(mIsFullScreen
);
2235 mozilla::WindowRenderer
* nsWindow::GetWindowRenderer() {
2236 if (!mWindowRenderer
) {
2237 CreateLayerManager();
2240 return mWindowRenderer
;
2243 void nsWindow::CreateLayerManager() {
2244 if (mWindowRenderer
) {
2248 nsWindow
* topLevelWindow
= FindTopLevel();
2249 if (!topLevelWindow
|| topLevelWindow
->mWindowType
== eWindowType_invisible
) {
2250 // don't create a layer manager for an invisible top-level window
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()) {
2263 "LayerViewSupport::NotifyCompositorCreated",
2264 [lvs
= mLayerViewSupport
,
2265 uiCompositorController
= GetUiCompositorControllerChild()] {
2266 if (auto lvsAccess
{lvs
.Access()}) {
2267 lvsAccess
->NotifyCompositorCreated(uiCompositorController
);
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();
2299 void nsWindow::ShowDynamicToolbar() {
2300 auto acc(mGeckoViewSupport
.Access());
2305 acc
->OnShowDynamicToolbar();
2308 void nsWindow::OnSizeChanged(const gfx::IntSize
& aSize
) {
2309 ALOG("nsWindow: %p OnSizeChanged [%d %d]", (void*)this, aSize
.width
,
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
) {
2323 event
.mRefPoint
= *aPoint
;
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
);
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
);
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
:
2371 case NS_NATIVE_WIDGET
:
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();
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());
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.
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");
2452 mIdleService
->ResetIdleTimeOut(0);
2455 if (FindTopLevel() != nsWindow::TopWindow()) {
2460 RefPtr
<mozilla::a11y::SessionAccessibility
>
2461 nsWindow::GetSessionAccessibility() {
2462 auto acc(mSessionAccessibility
.Access());
2467 return acc
.AsRefPtr();
2470 TextEventDispatcherListener
* nsWindow::GetNativeTextEventDispatcherListener() {
2471 nsWindow
* top
= FindTopLevel();
2474 auto acc(top
->mEditableSupport
.Access());
2476 // Non-GeckoView windows don't support IME operations.
2480 nsCOMPtr
<TextEventDispatcherListener
> ptr
;
2481 if (NS_FAILED(acc
->QueryInterface(NS_GET_IID(TextEventDispatcherListener
),
2482 getter_AddRefs(ptr
)))) {
2489 void nsWindow::SetInputContext(const InputContext
& aContext
,
2490 const InputContextAction
& aAction
) {
2491 nsWindow
* top
= FindTopLevel();
2494 auto acc(top
->mEditableSupport
.Access());
2496 // Non-GeckoView windows don't support IME operations.
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();
2511 auto acc(top
->mEditableSupport
.Access());
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");
2531 switch (aPointerState
) {
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
;
2538 // This could be turned into a ACTION_UP in Java
2539 eventType
= java::sdk::MotionEvent::ACTION_POINTER_UP
;
2542 eventType
= java::sdk::MotionEvent::ACTION_CANCEL
;
2544 case TOUCH_HOVER
: // not supported for now
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
;
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
);
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
;
2589 case NativeMouseMessage::ButtonUp
:
2590 nativeMessage
= java::sdk::MotionEvent::ACTION_POINTER_UP
;
2592 case NativeMouseMessage::Move
:
2593 nativeMessage
= java::sdk::MotionEvent::ACTION_HOVER_MOVE
;
2596 MOZ_ASSERT_UNREACHABLE("Non supported mouse event on Android");
2597 return NS_ERROR_INVALID_ARG
;
2600 if (aNativeMessage
!= NativeMouseMessage::ButtonUp
) {
2602 case MouseButton::ePrimary
:
2603 button
= java::sdk::MotionEvent::BUTTON_PRIMARY
;
2605 case MouseButton::eMiddle
:
2606 button
= java::sdk::MotionEvent::BUTTON_TERTIARY
;
2608 case MouseButton::eSecondary
:
2609 button
= java::sdk::MotionEvent::BUTTON_SECONDARY
;
2611 case MouseButton::eX1
:
2612 button
= java::sdk::MotionEvent::BUTTON_BACK
;
2614 case MouseButton::eX2
:
2615 button
= java::sdk::MotionEvent::BUTTON_FORWARD
;
2618 if (aNativeMessage
== NativeMouseMessage::ButtonDown
) {
2619 MOZ_ASSERT_UNREACHABLE("Non supported mouse button type on Android");
2620 return NS_ERROR_INVALID_ARG
;
2626 // TODO (bug 1693237): Handle aModifierFlags.
2628 "nsWindow::SynthesizeNativeMouseEvent",
2629 [npzc
= java::PanZoomController::NativeProvider::GlobalRef(npzc
),
2630 nativeMessage
, aPoint
, button
] {
2631 npzc
->SynthesizeNativeMouseEvent(nativeMessage
, aPoint
.x
, aPoint
.y
,
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
) {
2646 mCompositorWidgetDelegate
= delegate
->AsPlatformSpecificDelegate();
2647 MOZ_ASSERT(mCompositorWidgetDelegate
,
2648 "nsWindow::SetCompositorWidgetDelegate called with a "
2649 "non-PlatformCompositorWidgetDelegate");
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()) {
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()
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
,
2729 void nsWindow::RecvScreenPixels(Shmem
&& aMem
, const ScreenIntSize
& aSize
,
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
) {
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()) {
2794 RefPtr
<DataSourceSurface
> destDataSurface
;
2796 nsIntSize size
= nsIWidget::CustomCursorSize(aCursor
);
2797 // prevent DoS attacks
2798 if (size
.width
> 128 || size
.height
> 128) {
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
)) {
2809 RefPtr
<DataSourceSurface
> srcDataSurface
= surface
->GetDataSurface();
2810 if (NS_WARN_IF(!srcDataSurface
)) {
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
)) {
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.
2838 case eCursor_standard
:
2839 // android.view.PointerIcon.TYPE_ARROW
2842 // android.view.PointerIcon.TYPE_WAIT
2844 case eCursor_select
:
2845 // android.view.PointerIcon.TYPE_TEXT;
2847 case eCursor_hyperlink
:
2848 // android.view.PointerIcon.TYPE_HAND
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
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
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
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
2872 case eCursor_crosshair
:
2873 // android.view.PointerIcon.TYPE_CROSSHAIR
2876 // android.view.PointerIcon.TYPE_ARROW
2879 // android.view.PointerIcon.TYPE_HELP
2882 // android.view.PointerIcon.TYPE_COPY
2885 // android.view.PointerIcon.TYPE_ALIAS
2887 case eCursor_context_menu
:
2888 // android.view.PointerIcon.TYPE_CONTEXT_MENU
2891 // android.view.PointerIcon.TYPE_CELL
2894 // android.view.PointerIcon.TYPE_GRAB
2896 case eCursor_grabbing
:
2897 // android.view.PointerIcon.TYPE_GRABBING
2899 case eCursor_spinning
:
2900 // android.view.PointerIcon.TYPE_WAIT
2902 case eCursor_zoom_in
:
2903 // android.view.PointerIcon.TYPE_ZOOM_IN
2905 case eCursor_zoom_out
:
2906 // android.view.PointerIcon.TYPE_ZOOM_OUT
2908 case eCursor_not_allowed
:
2909 // android.view.PointerIcon.TYPE_NO_DROP:
2911 case eCursor_no_drop
:
2912 // android.view.PointerIcon.TYPE_NO_DROP:
2914 case eCursor_vertical_text
:
2915 // android.view.PointerIcon.TYPE_VERTICAL_TEXT
2917 case eCursor_all_scroll
:
2918 // android.view.PointerIcon.TYPE_ALL_SCROLL
2921 // android.view.PointerIcon.TYPE_NULL
2924 NS_WARNING_ASSERTION(aCursor
, "Invalid cursor type");
2925 // android.view.PointerIcon.TYPE_ARROW
2930 void nsWindow::SetCursor(const Cursor
& aCursor
) {
2931 if (mozilla::jni::GetAPIVersion() < 24) {
2935 // Only change cursor if it's actually been changed
2936 if (!mUpdateCursor
&& mCursor
== aCursor
) {
2940 mUpdateCursor
= false;
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();
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
);