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 "GeckoViewOutputStream.h"
27 #include "GeckoViewSupport.h"
28 #include "GLContext.h"
29 #include "GLContextProvider.h"
30 #include "JavaBuiltins.h"
31 #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 "mozilla/EventForwards.h"
41 #include "nsAppShell.h"
42 #include "nsContentUtils.h"
43 #include "nsFocusManager.h"
44 #include "nsGkAtoms.h"
45 #include "nsGfxCIID.h"
46 #include "nsIDocShellTreeOwner.h"
47 #include "nsLayoutUtils.h"
48 #include "nsNetUtil.h"
49 #include "nsPrintfCString.h"
52 #include "nsThreadUtils.h"
53 #include "nsUserIdleService.h"
54 #include "nsViewManager.h"
55 #include "nsWidgetsCID.h"
58 #include "nsIWidgetListener.h"
59 #include "nsIWindowWatcher.h"
60 #include "nsIAppWindow.h"
61 #include "nsIPrintSettings.h"
62 #include "nsIPrintSettingsService.h"
64 #include "mozilla/Logging.h"
65 #include "mozilla/MiscEvents.h"
66 #include "mozilla/MouseEvents.h"
67 #include "mozilla/Preferences.h"
68 #include "mozilla/StaticPrefs_android.h"
69 #include "mozilla/StaticPrefs_ui.h"
70 #include "mozilla/TouchEvents.h"
71 #include "mozilla/WeakPtr.h"
72 #include "mozilla/WheelHandlingHelper.h" // for WheelDeltaAdjustmentStrategy
73 #include "mozilla/a11y/SessionAccessibility.h"
74 #include "mozilla/dom/BrowsingContext.h"
75 #include "mozilla/dom/BrowserHost.h"
76 #include "mozilla/dom/CanonicalBrowsingContext.h"
77 #include "mozilla/dom/ContentChild.h"
78 #include "mozilla/dom/ContentParent.h"
79 #include "mozilla/dom/MouseEventBinding.h"
80 #include "mozilla/gfx/2D.h"
81 #include "mozilla/gfx/DataSurfaceHelpers.h"
82 #include "mozilla/gfx/Logging.h"
83 #include "mozilla/gfx/Swizzle.h"
84 #include "mozilla/gfx/Types.h"
85 #include "mozilla/ipc/Shmem.h"
86 #include "mozilla/java/EventDispatcherWrappers.h"
87 #include "mozilla/java/GeckoAppShellWrappers.h"
88 #include "mozilla/java/GeckoEditableChildWrappers.h"
89 #include "mozilla/java/GeckoResultWrappers.h"
90 #include "mozilla/java/GeckoSessionNatives.h"
91 #include "mozilla/java/GeckoSystemStateListenerWrappers.h"
92 #include "mozilla/java/PanZoomControllerNatives.h"
93 #include "mozilla/java/SessionAccessibilityWrappers.h"
94 #include "mozilla/java/SurfaceControlManagerWrappers.h"
95 #include "mozilla/jni/NativesInlines.h"
96 #include "mozilla/layers/APZEventState.h"
97 #include "mozilla/layers/APZInputBridge.h"
98 #include "mozilla/layers/APZThreadUtils.h"
99 #include "mozilla/layers/CompositorBridgeChild.h"
100 #include "mozilla/layers/CompositorOGL.h"
101 #include "mozilla/layers/CompositorSession.h"
102 #include "mozilla/layers/LayersTypes.h"
103 #include "mozilla/layers/UiCompositorControllerChild.h"
104 #include "mozilla/layers/IAPZCTreeManager.h"
105 #include "mozilla/ProfilerLabels.h"
106 #include "mozilla/widget/AndroidVsync.h"
107 #include "mozilla/widget/Screen.h"
109 #define GVS_LOG(...) MOZ_LOG(sGVSupportLog, LogLevel::Warning, (__VA_ARGS__))
111 using namespace mozilla
;
112 using namespace mozilla::dom
;
113 using namespace mozilla::layers
;
114 using namespace mozilla::widget
;
115 using namespace mozilla::ipc
;
117 using mozilla::dom::ContentChild
;
118 using mozilla::dom::ContentParent
;
119 using mozilla::gfx::DataSourceSurface
;
120 using mozilla::gfx::IntSize
;
121 using mozilla::gfx::Matrix
;
122 using mozilla::gfx::SurfaceFormat
;
123 using mozilla::java::GeckoSession
;
124 using mozilla::java::sdk::IllegalStateException
;
125 using GeckoPrintException
= GeckoSession::GeckoPrintException
;
126 static mozilla::LazyLogModule
sGVSupportLog("GeckoViewSupport");
128 // All the toplevel windows that have been created; these are in
129 // stacking order, so the window at gTopLevelWindows[0] is the topmost
131 static nsTArray
<nsWindow
*> gTopLevelWindows
;
133 static bool sFailedToCreateGLContext
= false;
135 // Multitouch swipe thresholds in inches
136 static const double SWIPE_MAX_PINCH_DELTA_INCHES
= 0.4;
137 static const double SWIPE_MIN_DISTANCE_INCHES
= 0.6;
139 static const double kTouchResampleVsyncAdjustMs
= 5.0;
141 static const int32_t INPUT_RESULT_UNHANDLED
=
142 java::PanZoomController::INPUT_RESULT_UNHANDLED
;
143 static const int32_t INPUT_RESULT_HANDLED
=
144 java::PanZoomController::INPUT_RESULT_HANDLED
;
145 static const int32_t INPUT_RESULT_HANDLED_CONTENT
=
146 java::PanZoomController::INPUT_RESULT_HANDLED_CONTENT
;
147 static const int32_t INPUT_RESULT_IGNORED
=
148 java::PanZoomController::INPUT_RESULT_IGNORED
;
150 static const nsCString::size_type MAX_TOPLEVEL_DATA_URI_LEN
= 2 * 1024 * 1024;
152 // Unique ID given to each widget, to identify it for the
153 // CompositorSurfaceManager.
154 static std::atomic
<int32_t> sWidgetId
{0};
157 template <class Instance
, class Impl
>
158 std::enable_if_t
<jni::detail::NativePtrPicker
<Impl
>::value
==
159 jni::detail::NativePtrType::REFPTR
,
161 CallAttachNative(Instance aInstance
, Impl
* aImpl
) {
162 Impl::AttachNative(aInstance
, RefPtr
<Impl
>(aImpl
).get());
165 template <class Instance
, class Impl
>
166 std::enable_if_t
<jni::detail::NativePtrPicker
<Impl
>::value
==
167 jni::detail::NativePtrType::OWNING
,
169 CallAttachNative(Instance aInstance
, Impl
* aImpl
) {
170 Impl::AttachNative(aInstance
, UniquePtr
<Impl
>(aImpl
));
173 template <class Lambda
>
174 bool DispatchToUiThread(const char* aName
, Lambda
&& aLambda
) {
175 if (RefPtr
<nsThread
> uiThread
= GetAndroidUiThread()) {
176 uiThread
->Dispatch(NS_NewRunnableFunction(aName
, std::move(aLambda
)));
186 using WindowPtr
= jni::NativeWeakPtr
<GeckoViewSupport
>;
189 * PanZoomController handles its native calls on the UI thread, so make
190 * it separate from GeckoViewSupport.
192 class NPZCSupport final
193 : public java::PanZoomController::NativeProvider::Natives
<NPZCSupport
> {
195 java::PanZoomController::NativeProvider::WeakRef mNPZC
;
197 // Stores the returnResult of each pending motion event between
198 // HandleMotionEvent and FinishHandlingMotionEvent.
199 std::queue
<std::pair
<uint64_t, java::GeckoResult::GlobalRef
>>
200 mPendingMotionEventReturnResults
;
202 RefPtr
<AndroidVsync
> mAndroidVsync
;
203 TouchResampler mTouchResampler
;
204 int mPreviousButtons
= 0;
205 bool mListeningToVsync
= false;
207 // Only true if mAndroidVsync is non-null and the resampling pref is set.
208 bool mTouchResamplingEnabled
= false;
210 template <typename Lambda
>
211 class InputEvent final
: public nsAppShell::Event
{
212 java::PanZoomController::NativeProvider::GlobalRef mNPZC
;
216 InputEvent(const NPZCSupport
* aNPZCSupport
, Lambda
&& aLambda
)
217 : mNPZC(aNPZCSupport
->mNPZC
), mLambda(std::move(aLambda
)) {}
219 void Run() override
{
220 MOZ_ASSERT(NS_IsMainThread());
222 JNIEnv
* const env
= jni::GetGeckoThreadEnv();
223 const auto npzcSupportWeak
= GetNative(
224 java::PanZoomController::NativeProvider::LocalRef(env
, mNPZC
));
225 if (!npzcSupportWeak
) {
226 // We already shut down.
227 env
->ExceptionClear();
231 auto acc
= npzcSupportWeak
->Access();
233 // We already shut down.
234 env
->ExceptionClear();
238 auto win
= acc
->mWindow
.Access();
240 // We already shut down.
241 env
->ExceptionClear();
245 nsWindow
* const window
= win
->GetNsWindow();
247 // We already shut down.
248 env
->ExceptionClear();
252 window
->UserActivity();
253 return mLambda(window
);
256 bool IsUIEvent() const override
{ return true; }
259 class MOZ_HEAP_CLASS Observer final
: public AndroidVsync::Observer
{
261 static Observer
* Create(jni::NativeWeakPtr
<NPZCSupport
>&& aNPZCSupport
) {
262 return new Observer(std::move(aNPZCSupport
));
266 // Private constructor, part of a strategy to make sure
267 // we're only able to create these on the heap.
268 explicit Observer(jni::NativeWeakPtr
<NPZCSupport
>&& aNPZCSupport
)
269 : mNPZCSupport(std::move(aNPZCSupport
)) {}
271 void OnVsync(const TimeStamp
& aTimeStamp
) override
{
272 auto accessor
= mNPZCSupport
.Access();
278 accessor
->mTouchResampler
.NotifyFrame(
280 TimeDuration::FromMilliseconds(kTouchResampleVsyncAdjustMs
));
281 accessor
->ConsumeMotionEventsFromResampler();
284 void Dispose() override
{ delete this; }
286 jni::NativeWeakPtr
<NPZCSupport
> mNPZCSupport
;
289 Observer
* mObserver
= nullptr;
291 template <typename Lambda
>
292 void PostInputEvent(Lambda
&& aLambda
) {
293 // Use priority queue for input events.
294 nsAppShell::PostEvent(
295 MakeUnique
<InputEvent
<Lambda
>>(this, std::move(aLambda
)));
299 typedef java::PanZoomController::NativeProvider::Natives
<NPZCSupport
> Base
;
301 NPZCSupport(WindowPtr aWindow
,
302 const java::PanZoomController::NativeProvider::LocalRef
& aNPZC
)
303 : mWindow(aWindow
), mNPZC(aNPZC
) {
305 auto win(mWindow
.Access());
307 #endif // defined(DEBUG)
309 // Use vsync for touch resampling on API level 19 and above.
310 // See gfxAndroidPlatform::CreateGlobalHardwareVsyncSource() for comparison.
311 if (jni::GetAPIVersion() >= 19) {
312 mAndroidVsync
= AndroidVsync::GetInstance();
317 if (mListeningToVsync
) {
318 MOZ_RELEASE_ASSERT(mAndroidVsync
);
319 mAndroidVsync
->UnregisterObserver(mObserver
, AndroidVsync::INPUT
);
320 mListeningToVsync
= false;
324 using Base::AttachNative
;
325 using Base::DisposeNative
;
327 void OnWeakNonIntrusiveDetach(already_AddRefed
<Runnable
> aDisposer
) {
328 RefPtr
<Runnable
> disposer
= aDisposer
;
329 // There are several considerations when shutting down NPZC. 1) The
330 // Gecko thread may destroy NPZC at any time when nsWindow closes. 2)
331 // There may be pending events on the Gecko thread when NPZC is
332 // destroyed. 3) mWindow may not be available when the pending event
333 // runs. 4) The UI thread may destroy NPZC at any time when GeckoView
334 // is destroyed. 5) The UI thread may destroy NPZC at the same time as
335 // Gecko thread trying to destroy NPZC. 6) There may be pending calls
336 // on the UI thread when NPZC is destroyed. 7) mWindow may have been
337 // cleared on the Gecko thread when the pending call happens on the UI
340 // 1) happens through OnWeakNonIntrusiveDetach, which first notifies the UI
341 // thread through Destroy; Destroy then calls DisposeNative, which
342 // finally disposes the native instance back on the Gecko thread. Using
343 // Destroy to indirectly call DisposeNative here also solves 5), by
344 // making everything go through the UI thread, avoiding contention.
346 // 2) and 3) are solved by clearing mWindow, which signals to the
347 // pending event that we had shut down. In that case the event bails
348 // and does not touch mWindow.
350 // 4) happens through DisposeNative directly.
352 // 6) is solved by keeping a destroyed flag in the Java NPZC instance,
353 // and only make a pending call if the destroyed flag is not set.
355 // 7) is solved by taking a lock whenever mWindow is modified on the
356 // Gecko thread or accessed on the UI thread. That way, we don't
357 // release mWindow until the UI thread is done using it, thus avoiding
358 // the race condition.
360 if (RefPtr
<nsThread
> uiThread
= GetAndroidUiThread()) {
361 auto npzc
= java::PanZoomController::NativeProvider::GlobalRef(mNPZC
);
367 NS_NewRunnableFunction("NPZCSupport::OnWeakNonIntrusiveDetach",
368 [npzc
, disposer
= std::move(disposer
)] {
369 npzc
->SetAttached(false);
375 const java::PanZoomController::NativeProvider::Ref
& GetJavaNPZC() const {
380 void SetIsLongpressEnabled(bool aIsLongpressEnabled
) {
381 RefPtr
<IAPZCTreeManager
> controller
;
383 if (auto window
= mWindow
.Access()) {
384 nsWindow
* gkWindow
= window
->GetNsWindow();
386 controller
= gkWindow
->mAPZC
;
391 controller
->SetLongTapEnabled(aIsLongpressEnabled
);
395 int32_t HandleScrollEvent(int64_t aTime
, int32_t aMetaState
, float aX
,
396 float aY
, float aHScroll
, float aVScroll
) {
397 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
399 RefPtr
<IAPZCTreeManager
> controller
;
401 if (auto window
= mWindow
.Access()) {
402 nsWindow
* gkWindow
= window
->GetNsWindow();
404 controller
= gkWindow
->mAPZC
;
409 return INPUT_RESULT_UNHANDLED
;
412 ScreenPoint origin
= ScreenPoint(aX
, aY
);
414 if (StaticPrefs::ui_scrolling_negate_wheel_scroll()) {
415 aHScroll
= -aHScroll
;
416 aVScroll
= -aVScroll
;
419 ScrollWheelInput
input(
420 nsWindow::GetEventTimeStamp(aTime
), nsWindow::GetModifiers(aMetaState
),
421 ScrollWheelInput::SCROLLMODE_SMOOTH
,
422 ScrollWheelInput::SCROLLDELTA_PIXEL
, origin
, aHScroll
, aVScroll
, false,
423 // XXX Do we need to support auto-dir scrolling
424 // for Android widgets with a wheel device?
425 // Currently, I just leave it unimplemented. If
426 // we need to implement it, what's the extra work
428 WheelDeltaAdjustmentStrategy::eNone
);
430 APZEventResult result
= controller
->InputBridge()->ReceiveInputEvent(input
);
431 if (result
.GetStatus() == nsEventStatus_eConsumeNoDefault
) {
432 return INPUT_RESULT_IGNORED
;
435 PostInputEvent([input
= std::move(input
), result
](nsWindow
* window
) {
436 WidgetWheelEvent wheelEvent
= input
.ToWidgetEvent(window
);
437 window
->ProcessUntransformedAPZEvent(&wheelEvent
, result
);
440 switch (result
.GetStatus()) {
441 case nsEventStatus_eIgnore
:
442 return INPUT_RESULT_UNHANDLED
;
443 case nsEventStatus_eConsumeDoDefault
:
444 return result
.GetHandledResult()->IsHandledByRoot()
445 ? INPUT_RESULT_HANDLED
446 : INPUT_RESULT_HANDLED_CONTENT
;
448 MOZ_ASSERT_UNREACHABLE("Unexpected nsEventStatus");
449 return INPUT_RESULT_UNHANDLED
;
454 static MouseInput::ButtonType
GetButtonType(int button
) {
455 MouseInput::ButtonType result
= MouseInput::NONE
;
458 case java::sdk::MotionEvent::BUTTON_PRIMARY
:
459 result
= MouseInput::PRIMARY_BUTTON
;
461 case java::sdk::MotionEvent::BUTTON_SECONDARY
:
462 result
= MouseInput::SECONDARY_BUTTON
;
464 case java::sdk::MotionEvent::BUTTON_TERTIARY
:
465 result
= MouseInput::MIDDLE_BUTTON
;
474 static int16_t ConvertButtons(int buttons
) {
477 if (buttons
& java::sdk::MotionEvent::BUTTON_PRIMARY
) {
478 result
|= MouseButtonsFlag::ePrimaryFlag
;
480 if (buttons
& java::sdk::MotionEvent::BUTTON_SECONDARY
) {
481 result
|= MouseButtonsFlag::eSecondaryFlag
;
483 if (buttons
& java::sdk::MotionEvent::BUTTON_TERTIARY
) {
484 result
|= MouseButtonsFlag::eMiddleFlag
;
486 if (buttons
& java::sdk::MotionEvent::BUTTON_BACK
) {
487 result
|= MouseButtonsFlag::e4thFlag
;
489 if (buttons
& java::sdk::MotionEvent::BUTTON_FORWARD
) {
490 result
|= MouseButtonsFlag::e5thFlag
;
496 static int32_t ConvertAPZHandledPlace(APZHandledPlace aHandledPlace
) {
497 switch (aHandledPlace
) {
498 case APZHandledPlace::Unhandled
:
499 return INPUT_RESULT_UNHANDLED
;
500 case APZHandledPlace::HandledByRoot
:
501 return INPUT_RESULT_HANDLED
;
502 case APZHandledPlace::HandledByContent
:
503 return INPUT_RESULT_HANDLED_CONTENT
;
504 case APZHandledPlace::Invalid
:
505 MOZ_ASSERT_UNREACHABLE("The handled result should NOT be Invalid");
506 return INPUT_RESULT_UNHANDLED
;
508 MOZ_ASSERT_UNREACHABLE("Unknown handled result");
509 return INPUT_RESULT_UNHANDLED
;
512 static int32_t ConvertSideBits(SideBits aSideBits
) {
513 int32_t ret
= java::PanZoomController::SCROLLABLE_FLAG_NONE
;
514 if (aSideBits
& SideBits::eTop
) {
515 ret
|= java::PanZoomController::SCROLLABLE_FLAG_TOP
;
517 if (aSideBits
& SideBits::eRight
) {
518 ret
|= java::PanZoomController::SCROLLABLE_FLAG_RIGHT
;
520 if (aSideBits
& SideBits::eBottom
) {
521 ret
|= java::PanZoomController::SCROLLABLE_FLAG_BOTTOM
;
523 if (aSideBits
& SideBits::eLeft
) {
524 ret
|= java::PanZoomController::SCROLLABLE_FLAG_LEFT
;
529 static int32_t ConvertScrollDirections(
530 layers::ScrollDirections aScrollDirections
) {
531 int32_t ret
= java::PanZoomController::OVERSCROLL_FLAG_NONE
;
532 if (aScrollDirections
.contains(layers::HorizontalScrollDirection
)) {
533 ret
|= java::PanZoomController::OVERSCROLL_FLAG_HORIZONTAL
;
535 if (aScrollDirections
.contains(layers::VerticalScrollDirection
)) {
536 ret
|= java::PanZoomController::OVERSCROLL_FLAG_VERTICAL
;
541 static java::PanZoomController::InputResultDetail::LocalRef
542 ConvertAPZHandledResult(const APZHandledResult
& aHandledResult
) {
543 return java::PanZoomController::InputResultDetail::New(
544 ConvertAPZHandledPlace(aHandledResult
.mPlace
),
545 ConvertSideBits(aHandledResult
.mScrollableDirections
),
546 ConvertScrollDirections(aHandledResult
.mOverscrollDirections
));
550 int32_t HandleMouseEvent(int32_t aAction
, int64_t aTime
, int32_t aMetaState
,
551 float aX
, float aY
, int buttons
) {
552 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
554 RefPtr
<IAPZCTreeManager
> controller
;
556 if (auto window
= mWindow
.Access()) {
557 nsWindow
* gkWindow
= window
->GetNsWindow();
559 controller
= gkWindow
->mAPZC
;
564 return INPUT_RESULT_UNHANDLED
;
567 MouseInput::MouseType mouseType
= MouseInput::MOUSE_NONE
;
568 MouseInput::ButtonType buttonType
= MouseInput::NONE
;
570 case java::sdk::MotionEvent::ACTION_DOWN
:
571 mouseType
= MouseInput::MOUSE_DOWN
;
572 buttonType
= GetButtonType(buttons
^ mPreviousButtons
);
573 mPreviousButtons
= buttons
;
575 case java::sdk::MotionEvent::ACTION_UP
:
576 mouseType
= MouseInput::MOUSE_UP
;
577 buttonType
= GetButtonType(buttons
^ mPreviousButtons
);
578 mPreviousButtons
= buttons
;
580 case java::sdk::MotionEvent::ACTION_MOVE
:
581 mouseType
= MouseInput::MOUSE_MOVE
;
583 case java::sdk::MotionEvent::ACTION_HOVER_MOVE
:
584 mouseType
= MouseInput::MOUSE_MOVE
;
586 case java::sdk::MotionEvent::ACTION_HOVER_ENTER
:
587 mouseType
= MouseInput::MOUSE_WIDGET_ENTER
;
589 case java::sdk::MotionEvent::ACTION_HOVER_EXIT
:
590 mouseType
= MouseInput::MOUSE_WIDGET_EXIT
;
596 if (mouseType
== MouseInput::MOUSE_NONE
) {
597 return INPUT_RESULT_UNHANDLED
;
600 ScreenPoint origin
= ScreenPoint(aX
, aY
);
603 mouseType
, buttonType
, MouseEvent_Binding::MOZ_SOURCE_MOUSE
,
604 ConvertButtons(buttons
), origin
, nsWindow::GetEventTimeStamp(aTime
),
605 nsWindow::GetModifiers(aMetaState
));
607 APZEventResult result
= controller
->InputBridge()->ReceiveInputEvent(input
);
608 if (result
.GetStatus() == nsEventStatus_eConsumeNoDefault
) {
609 return INPUT_RESULT_IGNORED
;
612 PostInputEvent([input
= std::move(input
), result
](nsWindow
* window
) {
613 WidgetMouseEvent mouseEvent
= input
.ToWidgetEvent(window
);
614 window
->ProcessUntransformedAPZEvent(&mouseEvent
, result
);
617 switch (result
.GetStatus()) {
618 case nsEventStatus_eIgnore
:
619 return INPUT_RESULT_UNHANDLED
;
620 case nsEventStatus_eConsumeDoDefault
:
621 return result
.GetHandledResult()->IsHandledByRoot()
622 ? INPUT_RESULT_HANDLED
623 : INPUT_RESULT_HANDLED_CONTENT
;
625 MOZ_ASSERT_UNREACHABLE("Unexpected nsEventStatus");
626 return INPUT_RESULT_UNHANDLED
;
630 // Convert MotionEvent touch radius and orientation into the format required
631 // by w3c touchevents.
632 // toolMajor and toolMinor span a rectangle that's oriented as per
633 // aOrientation, centered around the touch point.
634 static std::pair
<float, ScreenSize
> ConvertOrientationAndRadius(
635 float aOrientation
, float aToolMajor
, float aToolMinor
) {
636 float angle
= aOrientation
* 180.0f
/ M_PI
;
637 // w3c touchevents spec does not allow orientations == 90
638 // this shifts it to -90, which will be shifted to zero below
643 // w3c touchevent radii are given with an orientation between 0 and
644 // 90. The radii are found by removing the orientation and
645 // measuring the x and y radii of the resulting ellipse. For
646 // Android orientations >= 0 and < 90, use the y radius as the
647 // major radius, and x as the minor radius. However, for an
648 // orientation < 0, we have to shift the orientation by adding 90,
649 // and reverse which radius is major and minor.
654 ScreenSize(int32_t(aToolMajor
/ 2.0f
), int32_t(aToolMinor
/ 2.0f
));
657 ScreenSize(int32_t(aToolMinor
/ 2.0f
), int32_t(aToolMajor
/ 2.0f
));
660 return std::make_pair(angle
, radius
);
663 void HandleMotionEvent(
664 const java::PanZoomController::NativeProvider::LocalRef
& aInstance
,
665 jni::Object::Param aEventData
, float aScreenX
, float aScreenY
,
666 jni::Object::Param aResult
) {
667 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
669 auto returnResult
= java::GeckoResult::Ref::From(aResult
);
671 java::PanZoomController::MotionEventData::Ref::From(aEventData
);
672 nsTArray
<int32_t> pointerId(eventData
->PointerId()->GetElements());
673 size_t pointerCount
= pointerId
.Length();
674 MultiTouchInput::MultiTouchType type
;
675 size_t startIndex
= 0;
676 size_t endIndex
= pointerCount
;
678 switch (eventData
->Action()) {
679 case java::sdk::MotionEvent::ACTION_DOWN
:
680 case java::sdk::MotionEvent::ACTION_POINTER_DOWN
:
681 type
= MultiTouchInput::MULTITOUCH_START
;
683 case java::sdk::MotionEvent::ACTION_MOVE
:
684 type
= MultiTouchInput::MULTITOUCH_MOVE
;
686 case java::sdk::MotionEvent::ACTION_UP
:
687 case java::sdk::MotionEvent::ACTION_POINTER_UP
:
688 // for pointer-up events we only want the data from
689 // the one pointer that went up
690 type
= MultiTouchInput::MULTITOUCH_END
;
691 startIndex
= eventData
->ActionIndex();
692 endIndex
= startIndex
+ 1;
694 case java::sdk::MotionEvent::ACTION_OUTSIDE
:
695 case java::sdk::MotionEvent::ACTION_CANCEL
:
696 type
= MultiTouchInput::MULTITOUCH_CANCEL
;
700 returnResult
->Complete(
701 java::sdk::Integer::ValueOf(INPUT_RESULT_UNHANDLED
));
706 MultiTouchInput
input(type
, eventData
->Time(),
707 nsWindow::GetEventTimeStamp(eventData
->Time()), 0);
708 input
.modifiers
= nsWindow::GetModifiers(eventData
->MetaState());
709 input
.mTouches
.SetCapacity(endIndex
- startIndex
);
710 input
.mScreenOffset
=
711 ExternalIntPoint(int32_t(floorf(aScreenX
)), int32_t(floorf(aScreenY
)));
713 size_t historySize
= eventData
->HistorySize();
714 nsTArray
<int64_t> historicalTime(
715 eventData
->HistoricalTime()->GetElements());
716 MOZ_RELEASE_ASSERT(historicalTime
.Length() == historySize
);
718 // Each of these is |historySize| sets of |pointerCount| values.
719 size_t historicalDataCount
= historySize
* pointerCount
;
720 nsTArray
<float> historicalX(eventData
->HistoricalX()->GetElements());
721 nsTArray
<float> historicalY(eventData
->HistoricalY()->GetElements());
722 nsTArray
<float> historicalOrientation(
723 eventData
->HistoricalOrientation()->GetElements());
724 nsTArray
<float> historicalPressure(
725 eventData
->HistoricalPressure()->GetElements());
726 nsTArray
<float> historicalToolMajor(
727 eventData
->HistoricalToolMajor()->GetElements());
728 nsTArray
<float> historicalToolMinor(
729 eventData
->HistoricalToolMinor()->GetElements());
731 MOZ_RELEASE_ASSERT(historicalX
.Length() == historicalDataCount
);
732 MOZ_RELEASE_ASSERT(historicalY
.Length() == historicalDataCount
);
733 MOZ_RELEASE_ASSERT(historicalOrientation
.Length() == historicalDataCount
);
734 MOZ_RELEASE_ASSERT(historicalPressure
.Length() == historicalDataCount
);
735 MOZ_RELEASE_ASSERT(historicalToolMajor
.Length() == historicalDataCount
);
736 MOZ_RELEASE_ASSERT(historicalToolMinor
.Length() == historicalDataCount
);
738 // Each of these is |pointerCount| values.
739 nsTArray
<float> x(eventData
->X()->GetElements());
740 nsTArray
<float> y(eventData
->Y()->GetElements());
741 nsTArray
<float> orientation(eventData
->Orientation()->GetElements());
742 nsTArray
<float> pressure(eventData
->Pressure()->GetElements());
743 nsTArray
<float> toolMajor(eventData
->ToolMajor()->GetElements());
744 nsTArray
<float> toolMinor(eventData
->ToolMinor()->GetElements());
746 MOZ_ASSERT(x
.Length() == pointerCount
);
747 MOZ_ASSERT(y
.Length() == pointerCount
);
748 MOZ_ASSERT(orientation
.Length() == pointerCount
);
749 MOZ_ASSERT(pressure
.Length() == pointerCount
);
750 MOZ_ASSERT(toolMajor
.Length() == pointerCount
);
751 MOZ_ASSERT(toolMinor
.Length() == pointerCount
);
753 for (size_t i
= startIndex
; i
< endIndex
; i
++) {
754 auto [orien
, radius
] = ConvertOrientationAndRadius(
755 orientation
[i
], toolMajor
[i
], toolMinor
[i
]);
757 ScreenIntPoint
point(int32_t(floorf(x
[i
])), int32_t(floorf(y
[i
])));
758 SingleTouchData
singleTouchData(pointerId
[i
], point
, radius
, orien
,
761 for (size_t historyIndex
= 0; historyIndex
< historySize
;
763 size_t historicalI
= historyIndex
* pointerCount
+ i
;
764 auto [historicalAngle
, historicalRadius
] = ConvertOrientationAndRadius(
765 historicalOrientation
[historicalI
],
766 historicalToolMajor
[historicalI
], historicalToolMinor
[historicalI
]);
767 ScreenIntPoint
historicalPoint(
768 int32_t(floorf(historicalX
[historicalI
])),
769 int32_t(floorf(historicalY
[historicalI
])));
770 singleTouchData
.mHistoricalData
.AppendElement(
771 SingleTouchData::HistoricalTouchData
{
772 nsWindow::GetEventTimeStamp(historicalTime
[historyIndex
]),
774 {}, // mLocalScreenPoint will be computed later by APZ
777 historicalPressure
[historicalI
]});
780 input
.mTouches
.AppendElement(singleTouchData
);
784 eventData
->Action() == java::sdk::MotionEvent::ACTION_DOWN
) {
785 // Query pref value at the beginning of a touch gesture so that we don't
786 // leave events stuck in the resampler after a pref flip.
787 mTouchResamplingEnabled
= StaticPrefs::android_touch_resampling_enabled();
790 if (!mTouchResamplingEnabled
) {
791 FinishHandlingMotionEvent(std::move(input
),
792 java::GeckoResult::LocalRef(returnResult
));
796 uint64_t eventId
= mTouchResampler
.ProcessEvent(std::move(input
));
797 mPendingMotionEventReturnResults
.push(
798 {eventId
, java::GeckoResult::GlobalRef(returnResult
)});
800 RegisterOrUnregisterForVsync(mTouchResampler
.InTouchingState());
801 ConsumeMotionEventsFromResampler();
804 void RegisterOrUnregisterForVsync(bool aNeedVsync
) {
805 MOZ_RELEASE_ASSERT(mAndroidVsync
);
806 if (aNeedVsync
&& !mListeningToVsync
) {
807 MOZ_ASSERT(!mObserver
);
808 auto win
= mWindow
.Access();
812 RefPtr
<nsWindow
> gkWindow
= win
->GetNsWindow();
816 MutexAutoLock
lock(gkWindow
->GetDestroyMutex());
817 if (gkWindow
->Destroyed()) {
820 jni::NativeWeakPtr
<NPZCSupport
> weakPtrToThis
=
821 gkWindow
->GetNPZCSupportWeakPtr();
822 mObserver
= Observer::Create(std::move(weakPtrToThis
));
823 mAndroidVsync
->RegisterObserver(mObserver
, AndroidVsync::INPUT
);
824 } else if (!aNeedVsync
&& mListeningToVsync
) {
825 mAndroidVsync
->UnregisterObserver(mObserver
, AndroidVsync::INPUT
);
828 mListeningToVsync
= aNeedVsync
;
831 void ConsumeMotionEventsFromResampler() {
832 auto outgoing
= mTouchResampler
.ConsumeOutgoingEvents();
833 while (!outgoing
.empty()) {
834 auto outgoingEvent
= std::move(outgoing
.front());
836 java::GeckoResult::GlobalRef returnResult
;
837 if (outgoingEvent
.mEventId
) {
838 // Look up the GeckoResult for this event.
839 // The outgoing events from the resampler are in the same order as the
840 // original events, and no event IDs are skipped.
841 MOZ_RELEASE_ASSERT(!mPendingMotionEventReturnResults
.empty());
842 auto pair
= mPendingMotionEventReturnResults
.front();
843 mPendingMotionEventReturnResults
.pop();
844 MOZ_RELEASE_ASSERT(pair
.first
== *outgoingEvent
.mEventId
);
845 returnResult
= pair
.second
;
847 FinishHandlingMotionEvent(std::move(outgoingEvent
.mEvent
),
848 java::GeckoResult::LocalRef(returnResult
));
852 void FinishHandlingMotionEvent(MultiTouchInput
&& aInput
,
853 java::GeckoResult::LocalRef
&& aReturnResult
) {
854 RefPtr
<IAPZCTreeManager
> controller
;
856 if (auto window
= mWindow
.Access()) {
857 nsWindow
* gkWindow
= window
->GetNsWindow();
859 controller
= gkWindow
->mAPZC
;
865 aReturnResult
->Complete(java::PanZoomController::InputResultDetail::New(
866 INPUT_RESULT_UNHANDLED
,
867 java::PanZoomController::SCROLLABLE_FLAG_NONE
,
868 java::PanZoomController::OVERSCROLL_FLAG_NONE
));
873 APZInputBridge::InputBlockCallback callback
;
875 callback
= [aReturnResult
= java::GeckoResult::GlobalRef(aReturnResult
)](
876 uint64_t aInputBlockId
,
877 const APZHandledResult
& aHandledResult
) {
878 aReturnResult
->Complete(ConvertAPZHandledResult(aHandledResult
));
881 APZEventResult result
= controller
->InputBridge()->ReceiveInputEvent(
882 aInput
, std::move(callback
));
884 if (result
.GetStatus() == nsEventStatus_eConsumeNoDefault
) {
886 if (result
.GetHandledResult() != Nothing()) {
887 aReturnResult
->Complete(
888 ConvertAPZHandledResult(result
.GetHandledResult().value()));
890 MOZ_ASSERT_UNREACHABLE(
891 "nsEventStatus_eConsumeNoDefault should involve a valid "
893 aReturnResult
->Complete(
894 java::PanZoomController::InputResultDetail::New(
895 INPUT_RESULT_IGNORED
,
896 java::PanZoomController::SCROLLABLE_FLAG_NONE
,
897 java::PanZoomController::OVERSCROLL_FLAG_NONE
));
903 // Dispatch APZ input event on Gecko thread.
904 PostInputEvent([input
= std::move(aInput
), result
](nsWindow
* window
) {
905 WidgetTouchEvent touchEvent
= input
.ToWidgetEvent(window
);
906 window
->ProcessUntransformedAPZEvent(&touchEvent
, result
);
907 window
->DispatchHitTest(touchEvent
);
910 if (aReturnResult
&& result
.GetHandledResult() != Nothing()) {
911 MOZ_ASSERT(result
.GetStatus() == nsEventStatus_eConsumeDoDefault
||
912 result
.GetStatus() == nsEventStatus_eIgnore
);
913 aReturnResult
->Complete(
914 ConvertAPZHandledResult(result
.GetHandledResult().value()));
919 NS_IMPL_ISUPPORTS(AndroidView
, nsIAndroidEventDispatcher
, nsIAndroidView
)
921 nsresult
AndroidView::GetInitData(JSContext
* aCx
,
922 JS::MutableHandle
<JS::Value
> aOut
) {
928 return widget::EventDispatcher::UnboxBundle(aCx
, mInitData
, aOut
);
932 * Compositor has some unique requirements for its native calls, so make it
933 * separate from GeckoViewSupport.
935 class LayerViewSupport final
936 : public GeckoSession::Compositor::Natives
<LayerViewSupport
> {
938 GeckoSession::Compositor::WeakRef mCompositor
;
939 Atomic
<bool, ReleaseAcquire
> mCompositorPaused
;
940 java::sdk::Surface::GlobalRef mSurface
;
941 java::sdk::SurfaceControl::GlobalRef mSurfaceControl
;
946 // Used to communicate with the gecko compositor from the UI thread.
947 // Set in NotifyCompositorCreated and cleared in
948 // NotifyCompositorSessionLost.
949 RefPtr
<UiCompositorControllerChild
> mUiCompositorControllerChild
;
950 // Whether we have requested a new Surface from the GeckoSession.
951 bool mRequestedNewSurface
= false;
953 Maybe
<uint32_t> mDefaultClearColor
;
955 struct CaptureRequest
{
956 explicit CaptureRequest() : mResult(nullptr) {}
957 explicit CaptureRequest(java::GeckoResult::GlobalRef aResult
,
958 java::sdk::Bitmap::GlobalRef aBitmap
,
959 const ScreenRect
& aSource
,
960 const IntSize
& aOutputSize
)
964 mOutputSize(aOutputSize
) {}
966 // where to send the pixels
967 java::GeckoResult::GlobalRef mResult
;
969 // where to store the pixels
970 java::sdk::Bitmap::GlobalRef mBitmap
;
976 std::queue
<CaptureRequest
> mCapturePixelsResults
;
978 // In order to use Event::HasSameTypeAs in PostTo(), we cannot make
979 // LayerViewEvent a template because each template instantiation is
980 // a different type. So implement LayerViewEvent as a ProxyEvent.
981 class LayerViewEvent final
: public nsAppShell::ProxyEvent
{
982 using Event
= nsAppShell::Event
;
985 static UniquePtr
<Event
> MakeEvent(UniquePtr
<Event
>&& event
) {
986 return MakeUnique
<LayerViewEvent
>(std::move(event
));
989 explicit LayerViewEvent(UniquePtr
<Event
>&& event
)
990 : nsAppShell::ProxyEvent(std::move(event
)) {}
992 void PostTo(LinkedList
<Event
>& queue
) override
{
993 // Give priority to compositor events, but keep in order with
994 // existing compositor events.
995 nsAppShell::Event
* event
= queue
.getFirst();
996 while (event
&& event
->HasSameTypeAs(this)) {
997 event
= event
->getNext();
1000 event
->setPrevious(this);
1002 queue
.insertBack(this);
1008 typedef GeckoSession::Compositor::Natives
<LayerViewSupport
> Base
;
1010 LayerViewSupport(WindowPtr aWindow
,
1011 const GeckoSession::Compositor::LocalRef
& aInstance
)
1012 : mWindow(aWindow
), mCompositor(aInstance
), mCompositorPaused(true) {
1014 auto win(mWindow
.Access());
1016 #endif // defined(DEBUG)
1019 ~LayerViewSupport() {}
1021 using Base::AttachNative
;
1022 using Base::DisposeNative
;
1024 void OnWeakNonIntrusiveDetach(already_AddRefed
<Runnable
> aDisposer
) {
1025 RefPtr
<Runnable
> disposer
= aDisposer
;
1026 if (RefPtr
<nsThread
> uiThread
= GetAndroidUiThread()) {
1027 GeckoSession::Compositor::GlobalRef
compositor(mCompositor
);
1032 uiThread
->Dispatch(NS_NewRunnableFunction(
1033 "LayerViewSupport::OnWeakNonIntrusiveDetach",
1034 [compositor
, disposer
= std::move(disposer
),
1035 results
= &mCapturePixelsResults
, window
= mWindow
]() mutable {
1036 if (auto accWindow
= window
.Access()) {
1037 while (!results
->empty()) {
1039 java::GeckoResult::LocalRef(results
->front().mResult
);
1041 aResult
->CompleteExceptionally(
1042 java::sdk::IllegalStateException::New(
1043 "The compositor has detached from the session")
1044 .Cast
<jni::Throwable
>());
1050 compositor
->OnCompositorDetached();
1056 const GeckoSession::Compositor::Ref
& GetJavaCompositor() const {
1060 bool CompositorPaused() const { return mCompositorPaused
; }
1062 /// Called from the main thread whenever the compositor has been
1063 /// (re)initialized.
1064 void NotifyCompositorCreated(
1065 RefPtr
<UiCompositorControllerChild
> aUiCompositorControllerChild
) {
1066 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1067 mUiCompositorControllerChild
= aUiCompositorControllerChild
;
1069 if (mDefaultClearColor
) {
1070 mUiCompositorControllerChild
->SetDefaultClearColor(*mDefaultClearColor
);
1073 if (!mCompositorPaused
) {
1074 // If we are using SurfaceControl but mSurface is null, that means the
1075 // previous surface was destroyed along with the the previous
1076 // compositor, and we need to create a new one.
1077 if (mSurfaceControl
&& !mSurface
) {
1078 mSurface
= java::SurfaceControlManager::GetInstance()->GetChildSurface(
1079 mSurfaceControl
, mWidth
, mHeight
);
1082 if (auto window
{mWindow
.Access()}) {
1083 nsWindow
* gkWindow
= window
->GetNsWindow();
1085 mUiCompositorControllerChild
->OnCompositorSurfaceChanged(
1086 gkWindow
->mWidgetId
, mSurface
);
1090 bool resumed
= mUiCompositorControllerChild
->ResumeAndResize(
1091 mX
, mY
, mWidth
, mHeight
);
1094 << "Failed to resume compositor from NotifyCompositorCreated";
1095 RequestNewSurface();
1100 /// Called from the main thread whenever the compositor has been destroyed.
1101 void NotifyCompositorSessionLost() {
1102 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1103 mUiCompositorControllerChild
= nullptr;
1105 if (mSurfaceControl
) {
1106 // If we are using SurfaceControl then we must set the Surface to null
1107 // here to ensure we create a new one when the new compositor is
1112 if (auto window
= mWindow
.Access()) {
1113 while (!mCapturePixelsResults
.empty()) {
1115 java::GeckoResult::LocalRef(mCapturePixelsResults
.front().mResult
);
1117 result
->CompleteExceptionally(
1118 java::sdk::IllegalStateException::New(
1119 "Compositor session lost during screen pixels request")
1120 .Cast
<jni::Throwable
>());
1122 mCapturePixelsResults
.pop();
1127 java::sdk::Surface::Param
GetSurface() { return mSurface
; }
1130 already_AddRefed
<DataSourceSurface
> FlipScreenPixels(
1131 Shmem
& aMem
, const ScreenIntSize
& aInSize
, const ScreenRect
& aInRegion
,
1132 const IntSize
& aOutSize
) {
1133 RefPtr
<gfx::DataSourceSurface
> image
=
1134 gfx::Factory::CreateWrappingDataSourceSurface(
1135 aMem
.get
<uint8_t>(),
1136 StrideForFormatAndWidth(SurfaceFormat::B8G8R8A8
, aInSize
.width
),
1137 IntSize(aInSize
.width
, aInSize
.height
), SurfaceFormat::B8G8R8A8
);
1138 RefPtr
<gfx::DrawTarget
> drawTarget
=
1139 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
1140 aOutSize
, SurfaceFormat::B8G8R8A8
);
1145 drawTarget
->SetTransform(Matrix::Scaling(1.0, -1.0) *
1146 Matrix::Translation(0, aOutSize
.height
));
1148 gfx::Rect
srcRect(aInRegion
.x
,
1149 (aInSize
.height
- aInRegion
.height
) - aInRegion
.y
,
1150 aInRegion
.width
, aInRegion
.height
);
1151 gfx::Rect
destRect(0, 0, aOutSize
.width
, aOutSize
.height
);
1152 drawTarget
->DrawSurface(image
, destRect
, srcRect
);
1154 RefPtr
<gfx::SourceSurface
> snapshot
= drawTarget
->Snapshot();
1155 RefPtr
<gfx::DataSourceSurface
> data
= snapshot
->GetDataSurface();
1156 return data
.forget();
1160 * Compositor methods
1163 void AttachNPZC(jni::Object::Param aNPZC
) {
1164 MOZ_ASSERT(NS_IsMainThread());
1167 auto locked(mWindow
.Access());
1169 return; // Already shut down.
1172 nsWindow
* gkWindow
= locked
->GetNsWindow();
1174 // We can have this situation if we get two GeckoViewSupport::Transfer()
1175 // called before the first AttachNPZC() gets here. Just detach the current
1176 // instance since that's what happens in GeckoViewSupport::Transfer() as
1178 gkWindow
->mNPZCSupport
.Detach();
1180 auto npzc
= java::PanZoomController::NativeProvider::LocalRef(
1181 jni::GetGeckoThreadEnv(),
1182 java::PanZoomController::NativeProvider::Ref::From(aNPZC
));
1183 gkWindow
->mNPZCSupport
=
1184 jni::NativeWeakPtrHolder
<NPZCSupport
>::Attach(npzc
, mWindow
, npzc
);
1187 "LayerViewSupport::AttachNPZC",
1188 [npzc
= java::PanZoomController::NativeProvider::GlobalRef(npzc
)] {
1189 npzc
->SetAttached(true);
1193 void OnBoundsChanged(int32_t aLeft
, int32_t aTop
, int32_t aWidth
,
1195 MOZ_ASSERT(NS_IsMainThread());
1196 auto acc
= mWindow
.Access();
1198 return; // Already shut down.
1201 nsWindow
* gkWindow
= acc
->GetNsWindow();
1206 gkWindow
->Resize(aLeft
, aTop
, aWidth
, aHeight
, /* repaint */ false);
1209 void NotifyMemoryPressure() {
1210 MOZ_ASSERT(NS_IsMainThread());
1211 auto acc
= mWindow
.Access();
1213 return; // Already shut down.
1216 nsWindow
* gkWindow
= acc
->GetNsWindow();
1217 if (!gkWindow
|| !gkWindow
->mCompositorBridgeChild
) {
1221 gkWindow
->mCompositorBridgeChild
->SendNotifyMemoryPressure();
1224 void SetDynamicToolbarMaxHeight(int32_t aHeight
) {
1225 MOZ_ASSERT(NS_IsMainThread());
1226 auto acc
= mWindow
.Access();
1228 return; // Already shut down.
1231 nsWindow
* gkWindow
= acc
->GetNsWindow();
1236 gkWindow
->UpdateDynamicToolbarMaxHeight(ScreenIntCoord(aHeight
));
1239 void SyncPauseCompositor() {
1240 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1242 mCompositorPaused
= true;
1244 if (mUiCompositorControllerChild
) {
1245 mUiCompositorControllerChild
->Pause();
1248 mSurfaceControl
= nullptr;
1249 if (auto window
= mWindow
.Access()) {
1250 nsWindow
* gkWindow
= window
->GetNsWindow();
1252 mUiCompositorControllerChild
->OnCompositorSurfaceChanged(
1253 gkWindow
->mWidgetId
, nullptr);
1258 if (auto lock
{mWindow
.Access()}) {
1259 while (!mCapturePixelsResults
.empty()) {
1261 java::GeckoResult::LocalRef(mCapturePixelsResults
.front().mResult
);
1263 result
->CompleteExceptionally(
1264 java::sdk::IllegalStateException::New(
1265 "The compositor has detached from the session")
1266 .Cast
<jni::Throwable
>());
1268 mCapturePixelsResults
.pop();
1273 void SyncResumeCompositor() {
1274 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1276 if (mUiCompositorControllerChild
) {
1277 mCompositorPaused
= false;
1278 bool resumed
= mUiCompositorControllerChild
->Resume();
1281 << "Failed to resume compositor from SyncResumeCompositor";
1282 RequestNewSurface();
1287 void SyncResumeResizeCompositor(
1288 const GeckoSession::Compositor::LocalRef
& aObj
, int32_t aX
, int32_t aY
,
1289 int32_t aWidth
, int32_t aHeight
, jni::Object::Param aSurface
,
1290 jni::Object::Param aSurfaceControl
) {
1291 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1298 java::sdk::SurfaceControl::GlobalRef::From(aSurfaceControl
);
1299 if (mSurfaceControl
) {
1300 // When using SurfaceControl, we create a child Surface to render in to
1301 // rather than rendering directly in to the Surface provided by the
1302 // application. This allows us to work around a bug on some versions of
1303 // Android when recovering from a GPU process crash.
1304 mSurface
= java::SurfaceControlManager::GetInstance()->GetChildSurface(
1305 mSurfaceControl
, mWidth
, mHeight
);
1307 mSurface
= java::sdk::Surface::GlobalRef::From(aSurface
);
1310 if (mUiCompositorControllerChild
) {
1311 if (auto window
= mWindow
.Access()) {
1312 nsWindow
* gkWindow
= window
->GetNsWindow();
1314 // Send new Surface to GPU process, if one exists.
1315 mUiCompositorControllerChild
->OnCompositorSurfaceChanged(
1316 gkWindow
->mWidgetId
, mSurface
);
1320 bool resumed
= mUiCompositorControllerChild
->ResumeAndResize(
1321 aX
, aY
, aWidth
, aHeight
);
1324 << "Failed to resume compositor from SyncResumeResizeCompositor";
1325 // Only request a new Surface if this SyncResumeAndResize call is not
1326 // response to a previous request, otherwise we will get stuck in an
1328 if (!mRequestedNewSurface
) {
1329 RequestNewSurface();
1335 mRequestedNewSurface
= false;
1337 mCompositorPaused
= false;
1339 class OnResumedEvent
: public nsAppShell::Event
{
1340 GeckoSession::Compositor::GlobalRef mCompositor
;
1343 explicit OnResumedEvent(GeckoSession::Compositor::GlobalRef
&& aCompositor
)
1344 : mCompositor(std::move(aCompositor
)) {}
1346 void Run() override
{
1347 MOZ_ASSERT(NS_IsMainThread());
1349 JNIEnv
* const env
= jni::GetGeckoThreadEnv();
1350 const auto lvsHolder
=
1351 GetNative(GeckoSession::Compositor::LocalRef(env
, mCompositor
));
1354 env
->ExceptionClear();
1355 return; // Already shut down.
1358 auto lvs(lvsHolder
->Access());
1360 env
->ExceptionClear();
1361 return; // Already shut down.
1364 auto win
= lvs
->mWindow
.Access();
1366 env
->ExceptionClear();
1367 return; // Already shut down.
1370 // When we get here, the compositor has already been told to
1371 // resume. This means it's now safe for layer updates to occur.
1372 // Since we might have prevented one or more draw events from
1373 // occurring while the compositor was paused, we need to
1374 // schedule a draw event now.
1375 if (!lvs
->mCompositorPaused
) {
1376 nsWindow
* const gkWindow
= win
->GetNsWindow();
1378 gkWindow
->RedrawAll();
1384 // Use priority queue for timing-sensitive event.
1385 nsAppShell::PostEvent(
1386 MakeUnique
<LayerViewEvent
>(MakeUnique
<OnResumedEvent
>(aObj
)));
1389 void RequestNewSurface() {
1390 if (const auto& compositor
= GetJavaCompositor()) {
1391 mRequestedNewSurface
= true;
1392 if (mSurfaceControl
) {
1393 java::SurfaceControlManager::GetInstance()->RemoveSurface(
1396 compositor
->RequestNewSurface();
1400 mozilla::jni::Object::LocalRef
GetMagnifiableSurface() {
1401 return mozilla::jni::Object::LocalRef::From(GetSurface());
1404 void SyncInvalidateAndScheduleComposite() {
1405 if (!mUiCompositorControllerChild
) {
1409 if (AndroidBridge::IsJavaUiThread()) {
1410 mUiCompositorControllerChild
->InvalidateAndRender();
1414 if (RefPtr
<nsThread
> uiThread
= GetAndroidUiThread()) {
1415 uiThread
->Dispatch(NewRunnableMethod
<>(
1416 "LayerViewSupport::InvalidateAndRender",
1417 mUiCompositorControllerChild
,
1418 &UiCompositorControllerChild::InvalidateAndRender
),
1419 nsIThread::DISPATCH_NORMAL
);
1423 void SetMaxToolbarHeight(int32_t aHeight
) {
1424 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1426 if (mUiCompositorControllerChild
) {
1427 mUiCompositorControllerChild
->SetMaxToolbarHeight(aHeight
);
1431 void SetFixedBottomOffset(int32_t aOffset
) {
1432 if (auto acc
{mWindow
.Access()}) {
1433 nsWindow
* gkWindow
= acc
->GetNsWindow();
1435 gkWindow
->UpdateDynamicToolbarOffset(ScreenIntCoord(aOffset
));
1439 if (RefPtr
<nsThread
> uiThread
= GetAndroidUiThread()) {
1440 uiThread
->Dispatch(NS_NewRunnableFunction(
1441 "LayerViewSupport::SetFixedBottomOffset", [this, offset
= aOffset
] {
1442 if (mUiCompositorControllerChild
) {
1443 mUiCompositorControllerChild
->SetFixedBottomOffset(offset
);
1449 void SendToolbarAnimatorMessage(int32_t aMessage
) {
1450 if (!mUiCompositorControllerChild
) {
1454 if (AndroidBridge::IsJavaUiThread()) {
1455 mUiCompositorControllerChild
->ToolbarAnimatorMessageFromUI(aMessage
);
1459 if (RefPtr
<nsThread
> uiThread
= GetAndroidUiThread()) {
1461 NewRunnableMethod
<int32_t>(
1462 "LayerViewSupport::ToolbarAnimatorMessageFromUI",
1463 mUiCompositorControllerChild
,
1464 &UiCompositorControllerChild::ToolbarAnimatorMessageFromUI
,
1466 nsIThread::DISPATCH_NORMAL
);
1470 void RecvToolbarAnimatorMessage(int32_t aMessage
) {
1471 auto compositor
= GeckoSession::Compositor::LocalRef(mCompositor
);
1473 compositor
->RecvToolbarAnimatorMessage(aMessage
);
1477 void SetDefaultClearColor(int32_t aColor
) {
1478 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1479 mDefaultClearColor
= Some((uint32_t)aColor
);
1480 if (mUiCompositorControllerChild
) {
1481 mUiCompositorControllerChild
->SetDefaultClearColor((uint32_t)aColor
);
1485 void RequestScreenPixels(jni::Object::Param aResult
,
1486 jni::Object::Param aTarget
, int32_t aXOffset
,
1487 int32_t aYOffset
, int32_t aSrcWidth
,
1488 int32_t aSrcHeight
, int32_t aOutWidth
,
1489 int32_t aOutHeight
) {
1490 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1491 auto result
= java::GeckoResult::LocalRef(aResult
);
1493 if (!mUiCompositorControllerChild
) {
1495 if (auto window
= mWindow
.Access()) {
1496 result
->CompleteExceptionally(
1497 java::sdk::IllegalStateException::New(
1498 "Compositor session lost prior to screen pixels request")
1499 .Cast
<jni::Throwable
>());
1506 if (auto window
= mWindow
.Access()) {
1507 mCapturePixelsResults
.push(CaptureRequest(
1508 java::GeckoResult::GlobalRef(result
),
1509 java::sdk::Bitmap::GlobalRef(java::sdk::Bitmap::LocalRef(aTarget
)),
1510 ScreenRect(aXOffset
, aYOffset
, aSrcWidth
, aSrcHeight
),
1511 IntSize(aOutWidth
, aOutHeight
)));
1512 size
= mCapturePixelsResults
.size();
1516 mUiCompositorControllerChild
->RequestScreenPixels();
1520 void RecvScreenPixels(Shmem
&& aMem
, const ScreenIntSize
& aSize
,
1522 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1523 CaptureRequest request
;
1524 java::GeckoResult::LocalRef result
= nullptr;
1525 java::sdk::Bitmap::LocalRef bitmap
= nullptr;
1526 if (auto window
= mWindow
.Access()) {
1527 // The result might have been already rejected if the compositor was
1528 // detached from the session
1529 if (!mCapturePixelsResults
.empty()) {
1530 request
= mCapturePixelsResults
.front();
1531 result
= java::GeckoResult::LocalRef(request
.mResult
);
1532 bitmap
= java::sdk::Bitmap::LocalRef(request
.mBitmap
);
1533 mCapturePixelsResults
.pop();
1539 RefPtr
<DataSourceSurface
> surf
;
1541 surf
= FlipScreenPixels(aMem
, aSize
, request
.mSource
,
1542 request
.mOutputSize
);
1544 surf
= gfx::Factory::CreateWrappingDataSourceSurface(
1545 aMem
.get
<uint8_t>(),
1546 StrideForFormatAndWidth(SurfaceFormat::B8G8R8A8
, aSize
.width
),
1547 IntSize(aSize
.width
, aSize
.height
), SurfaceFormat::B8G8R8A8
);
1550 DataSourceSurface::ScopedMap
smap(surf
, DataSourceSurface::READ
);
1551 auto pixels
= mozilla::jni::ByteBuffer::New(
1552 reinterpret_cast<int8_t*>(smap
.GetData()),
1553 smap
.GetStride() * request
.mOutputSize
.height
);
1554 bitmap
->CopyPixelsFromBuffer(pixels
);
1555 result
->Complete(bitmap
);
1557 result
->CompleteExceptionally(
1558 java::sdk::IllegalStateException::New(
1559 "Failed to create flipped snapshot surface (probably out "
1561 .Cast
<jni::Throwable
>());
1564 result
->CompleteExceptionally(java::sdk::IllegalArgumentException::New(
1565 "No target bitmap argument provided")
1566 .Cast
<jni::Throwable
>());
1570 // Pixels have been copied, so Dealloc Shmem
1571 if (mUiCompositorControllerChild
) {
1572 mUiCompositorControllerChild
->DeallocPixelBuffer(aMem
);
1574 if (auto window
= mWindow
.Access()) {
1575 if (!mCapturePixelsResults
.empty()) {
1576 mUiCompositorControllerChild
->RequestScreenPixels();
1582 void EnableLayerUpdateNotifications(bool aEnable
) {
1583 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1584 if (mUiCompositorControllerChild
) {
1585 mUiCompositorControllerChild
->EnableLayerUpdateNotifications(aEnable
);
1589 void OnSafeAreaInsetsChanged(int32_t aTop
, int32_t aRight
, int32_t aBottom
,
1591 MOZ_ASSERT(NS_IsMainThread());
1592 auto win(mWindow
.Access());
1594 return; // Already shut down.
1597 nsWindow
* gkWindow
= win
->GetNsWindow();
1602 ScreenIntMargin
safeAreaInsets(aTop
, aRight
, aBottom
, aLeft
);
1603 gkWindow
->UpdateSafeAreaInsets(safeAreaInsets
);
1607 GeckoViewSupport::~GeckoViewSupport() {
1609 mWindow
->DetachNatives();
1614 void GeckoViewSupport::Open(
1615 const jni::Class::LocalRef
& aCls
, GeckoSession::Window::Param aWindow
,
1616 jni::Object::Param aQueue
, jni::Object::Param aCompositor
,
1617 jni::Object::Param aDispatcher
, jni::Object::Param aSessionAccessibility
,
1618 jni::Object::Param aInitData
, jni::String::Param aId
,
1619 jni::String::Param aChromeURI
, bool aPrivateMode
) {
1620 MOZ_ASSERT(NS_IsMainThread());
1622 AUTO_PROFILER_LABEL("mozilla::widget::GeckoViewSupport::Open", OTHER
);
1624 // We'll need gfxPlatform to be initialized to create a compositor later.
1625 // Might as well do that now so that the GPU process launch can get a head
1627 gfxPlatform::GetPlatform();
1629 nsCOMPtr
<nsIWindowWatcher
> ww
= do_GetService(NS_WINDOWWATCHER_CONTRACTID
);
1630 MOZ_RELEASE_ASSERT(ww
);
1634 url
= aChromeURI
->ToCString();
1636 nsresult rv
= Preferences::GetCString("toolkit.defaultChromeURI", url
);
1637 if (NS_FAILED(rv
)) {
1638 url
= "chrome://geckoview/content/geckoview.xhtml"_ns
;
1642 // Prepare an nsIAndroidView to pass as argument to the window.
1643 RefPtr
<AndroidView
> androidView
= new AndroidView();
1644 androidView
->mEventDispatcher
->Attach(
1645 java::EventDispatcher::Ref::From(aDispatcher
), nullptr);
1646 androidView
->mInitData
= java::GeckoBundle::Ref::From(aInitData
);
1648 nsAutoCString
chromeFlags("chrome,dialog=0,remote,resizable,scrollbars");
1650 chromeFlags
+= ",private";
1652 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
1653 ww
->OpenWindow(nullptr, url
, nsDependentCString(aId
->ToCString().get()),
1654 chromeFlags
, androidView
, getter_AddRefs(domWindow
));
1655 MOZ_RELEASE_ASSERT(domWindow
);
1657 nsCOMPtr
<nsPIDOMWindowOuter
> pdomWindow
= nsPIDOMWindowOuter::From(domWindow
);
1658 const RefPtr
<nsWindow
> window
= nsWindow::From(pdomWindow
);
1661 // Attach a new GeckoView support object to the new window.
1662 GeckoSession::Window::LocalRef
sessionWindow(aCls
.Env(), aWindow
);
1663 auto weakGeckoViewSupport
=
1664 jni::NativeWeakPtrHolder
<GeckoViewSupport
>::Attach(
1665 sessionWindow
, window
, sessionWindow
, pdomWindow
);
1667 window
->mGeckoViewSupport
= weakGeckoViewSupport
;
1668 window
->mAndroidView
= androidView
;
1670 // Attach other session support objects.
1671 { // Scope for gvsAccess
1672 auto gvsAccess
= weakGeckoViewSupport
.Access();
1673 MOZ_ASSERT(gvsAccess
);
1675 gvsAccess
->Transfer(sessionWindow
, aQueue
, aCompositor
, aDispatcher
,
1676 aSessionAccessibility
, aInitData
);
1679 if (window
->mWidgetListener
) {
1680 nsCOMPtr
<nsIAppWindow
> appWindow(window
->mWidgetListener
->GetAppWindow());
1682 // Our window is not intrinsically sized, so tell AppWindow to
1683 // not set a size for us.
1684 appWindow
->SetIntrinsicallySized(false);
1689 void GeckoViewSupport::Close() {
1691 if (mWindow
->mAndroidView
) {
1692 mWindow
->mAndroidView
->mEventDispatcher
->Detach();
1701 mDOMWindow
->ForceClose();
1702 mDOMWindow
= nullptr;
1703 mGeckoViewWindow
= nullptr;
1706 void GeckoViewSupport::Transfer(const GeckoSession::Window::LocalRef
& inst
,
1707 jni::Object::Param aQueue
,
1708 jni::Object::Param aCompositor
,
1709 jni::Object::Param aDispatcher
,
1710 jni::Object::Param aSessionAccessibility
,
1711 jni::Object::Param aInitData
) {
1712 mWindow
->mNPZCSupport
.Detach();
1714 auto compositor
= GeckoSession::Compositor::LocalRef(
1715 inst
.Env(), GeckoSession::Compositor::Ref::From(aCompositor
));
1718 { // Scope for lvsAccess
1719 auto lvsAccess
{mWindow
->mLayerViewSupport
.Access()};
1720 // If we do not yet have mLayerViewSupport, or if the compositor has
1721 // changed, then we must attach a new one.
1722 attachLvs
= !lvsAccess
|| lvsAccess
->GetJavaCompositor() != compositor
;
1726 mWindow
->mLayerViewSupport
=
1727 jni::NativeWeakPtrHolder
<LayerViewSupport
>::Attach(
1728 compositor
, mWindow
->mGeckoViewSupport
, compositor
);
1730 if (RefPtr
<UiCompositorControllerChild
> uiCompositorController
=
1731 mWindow
->GetUiCompositorControllerChild()) {
1733 "LayerViewSupport::NotifyCompositorCreated",
1734 [lvs
= mWindow
->mLayerViewSupport
, uiCompositorController
] {
1735 if (auto lvsAccess
{lvs
.Access()}) {
1736 lvsAccess
->NotifyCompositorCreated(uiCompositorController
);
1742 MOZ_ASSERT(mWindow
->mAndroidView
);
1743 mWindow
->mAndroidView
->mEventDispatcher
->Attach(
1744 java::EventDispatcher::Ref::From(aDispatcher
), mDOMWindow
);
1746 RefPtr
<jni::DetachPromise
> promise
= mWindow
->mSessionAccessibility
.Detach();
1747 if (aSessionAccessibility
) {
1748 // SessionAccessibility's JNI object isn't released immediately, it uses
1749 // recycled object, we have to wait for released object completely.
1750 auto sa
= java::SessionAccessibility::NativeProvider::LocalRef(
1751 aSessionAccessibility
);
1753 GetMainThreadSerialEventTarget(),
1754 "GeckoViewSupprt::Transfer::SessionAccessibility",
1755 [inst
= GeckoSession::Window::GlobalRef(inst
),
1756 sa
= java::SessionAccessibility::NativeProvider::GlobalRef(sa
),
1757 window
= mWindow
, gvs
= mWindow
->mGeckoViewSupport
](
1758 const mozilla::jni::DetachPromise::ResolveOrRejectValue
& aValue
) {
1759 MOZ_ASSERT(aValue
.IsResolve());
1760 if (window
->Destroyed()) {
1764 MOZ_ASSERT(!window
->mSessionAccessibility
.IsAttached());
1765 if (auto gvsAccess
{gvs
.Access()}) {
1766 gvsAccess
->AttachAccessibility(inst
, sa
);
1772 // We're in a transfer; update init-data and notify JS code.
1773 mWindow
->mAndroidView
->mInitData
= java::GeckoBundle::Ref::From(aInitData
);
1775 mWindow
->mAndroidView
->mEventDispatcher
->Dispatch(
1776 u
"GeckoView:UpdateInitData");
1779 DispatchToUiThread("GeckoViewSupport::Transfer",
1780 [compositor
= GeckoSession::Compositor::GlobalRef(
1781 compositor
)] { compositor
->OnCompositorAttached(); });
1784 void GeckoViewSupport::AttachEditable(
1785 const GeckoSession::Window::LocalRef
& inst
,
1786 jni::Object::Param aEditableParent
) {
1787 if (auto win
{mWindow
->mEditableSupport
.Access()}) {
1788 win
->TransferParent(aEditableParent
);
1790 auto editableChild
= java::GeckoEditableChild::New(aEditableParent
,
1791 /* default */ true);
1792 mWindow
->mEditableSupport
=
1793 jni::NativeWeakPtrHolder
<GeckoEditableSupport
>::Attach(
1794 editableChild
, mWindow
->mGeckoViewSupport
, editableChild
);
1797 mWindow
->mEditableParent
= aEditableParent
;
1800 void GeckoViewSupport::AttachAccessibility(
1801 const GeckoSession::Window::LocalRef
& inst
,
1802 jni::Object::Param aSessionAccessibility
) {
1803 java::SessionAccessibility::NativeProvider::LocalRef
sessionAccessibility(
1805 sessionAccessibility
= java::SessionAccessibility::NativeProvider::Ref::From(
1806 aSessionAccessibility
);
1808 mWindow
->mSessionAccessibility
=
1809 jni::NativeWeakPtrHolder
<a11y::SessionAccessibility
>::Attach(
1810 sessionAccessibility
, mWindow
->mGeckoViewSupport
,
1811 sessionAccessibility
);
1814 auto GeckoViewSupport::OnLoadRequest(mozilla::jni::String::Param aUri
,
1815 int32_t aWindowType
, int32_t aFlags
,
1816 mozilla::jni::String::Param aTriggeringUri
,
1817 bool aHasUserGesture
,
1818 bool aIsTopLevel
) const
1819 -> java::GeckoResult::LocalRef
{
1820 GeckoSession::Window::LocalRef
window(mGeckoViewWindow
);
1824 return window
->OnLoadRequest(aUri
, aWindowType
, aFlags
, aTriggeringUri
,
1825 aHasUserGesture
, aIsTopLevel
);
1828 void GeckoViewSupport::OnShowDynamicToolbar() const {
1829 GeckoSession::Window::LocalRef
window(mGeckoViewWindow
);
1834 window
->OnShowDynamicToolbar();
1837 void GeckoViewSupport::OnReady(jni::Object::Param aQueue
) {
1838 GeckoSession::Window::LocalRef
window(mGeckoViewWindow
);
1842 window
->OnReady(aQueue
);
1846 void GeckoViewSupport::PassExternalResponse(
1847 java::WebResponse::Param aResponse
) {
1848 GeckoSession::Window::LocalRef
window(mGeckoViewWindow
);
1853 auto response
= java::WebResponse::GlobalRef(aResponse
);
1855 DispatchToUiThread("GeckoViewSupport::PassExternalResponse",
1856 [window
= java::GeckoSession::Window::GlobalRef(window
),
1857 response
] { window
->PassExternalWebResponse(response
); });
1860 RefPtr
<CanonicalBrowsingContext
>
1861 GeckoViewSupport::GetContentCanonicalBrowsingContext() {
1862 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
= mDOMWindow
->GetTreeOwner();
1866 RefPtr
<BrowsingContext
> bc
;
1867 nsresult rv
= treeOwner
->GetPrimaryContentBrowsingContext(getter_AddRefs(bc
));
1868 if (NS_WARN_IF(NS_FAILED(rv
)) || !bc
) {
1871 return bc
->Canonical();
1874 void GeckoViewSupport::CreatePdf(
1875 jni::LocalRef
<mozilla::java::GeckoResult
> aGeckoResult
,
1876 RefPtr
<dom::CanonicalBrowsingContext
> aCbc
) {
1877 MOZ_ASSERT(NS_IsMainThread());
1878 const auto pdfErrorMsg
= "Could not save this page as PDF.";
1879 auto stream
= java::GeckoInputStream::New(nullptr);
1880 RefPtr
<GeckoViewOutputStream
> streamListener
=
1881 new GeckoViewOutputStream(stream
);
1883 nsCOMPtr
<nsIPrintSettingsService
> printSettingsService
=
1884 do_GetService("@mozilla.org/gfx/printsettings-service;1");
1885 if (!printSettingsService
) {
1886 aGeckoResult
->CompleteExceptionally(
1887 GeckoPrintException::New(
1888 GeckoPrintException::ERROR_PRINT_SETTINGS_SERVICE_NOT_AVAILABLE
)
1889 .Cast
<jni::Throwable
>());
1890 GVS_LOG("Could not create print settings service.");
1894 nsCOMPtr
<nsIPrintSettings
> printSettings
;
1895 nsresult rv
= printSettingsService
->CreateNewPrintSettings(
1896 getter_AddRefs(printSettings
));
1897 if (NS_WARN_IF(NS_FAILED(rv
))) {
1898 aGeckoResult
->CompleteExceptionally(
1899 GeckoPrintException::New(
1900 GeckoPrintException::ERROR_UNABLE_TO_CREATE_PRINT_SETTINGS
)
1901 .Cast
<jni::Throwable
>());
1902 GVS_LOG("Could not create print settings.");
1906 printSettings
->SetPrinterName(u
"Mozilla Save to PDF"_ns
);
1907 printSettings
->SetOutputDestination(
1908 nsIPrintSettings::kOutputDestinationStream
);
1909 printSettings
->SetOutputFormat(nsIPrintSettings::kOutputFormatPDF
);
1910 printSettings
->SetOutputStream(streamListener
);
1911 printSettings
->SetPrintSilent(true);
1913 RefPtr
<CanonicalBrowsingContext::PrintPromise
> print
=
1914 aCbc
->Print(printSettings
);
1916 aGeckoResult
->Complete(stream
);
1918 mozilla::GetCurrentSerialEventTarget(), __func__
,
1919 [result
= java::GeckoResult::GlobalRef(aGeckoResult
), stream
,
1921 const CanonicalBrowsingContext::PrintPromise::ResolveOrRejectValue
&
1923 if (aValue
.IsReject()) {
1924 GVS_LOG("Could not print. %s", pdfErrorMsg
);
1925 stream
->WriteError();
1930 void GeckoViewSupport::PrintToPdf(
1931 const java::GeckoSession::Window::LocalRef
& inst
,
1932 jni::Object::Param aResult
) {
1933 auto geckoResult
= java::GeckoResult::Ref::From(aResult
);
1934 RefPtr
<CanonicalBrowsingContext
> cbc
= GetContentCanonicalBrowsingContext();
1936 geckoResult
->CompleteExceptionally(
1937 GeckoPrintException::New(
1938 GeckoPrintException::
1939 ERROR_UNABLE_TO_RETRIEVE_CANONICAL_BROWSING_CONTEXT
)
1940 .Cast
<jni::Throwable
>());
1941 GVS_LOG("Could not retrieve content canonical browsing context.");
1944 CreatePdf(geckoResult
, cbc
);
1947 void GeckoViewSupport::PrintToPdf(
1948 const java::GeckoSession::Window::LocalRef
& inst
,
1949 jni::Object::Param aResult
, int64_t aBcId
) {
1950 auto geckoResult
= java::GeckoResult::Ref::From(aResult
);
1952 RefPtr
<CanonicalBrowsingContext
> cbc
= CanonicalBrowsingContext::Get(aBcId
);
1954 geckoResult
->CompleteExceptionally(
1955 GeckoPrintException::New(
1956 GeckoPrintException::
1957 ERROR_UNABLE_TO_RETRIEVE_CANONICAL_BROWSING_CONTEXT
)
1958 .Cast
<jni::Throwable
>());
1959 GVS_LOG("Could not retrieve content canonical browsing context by ID.");
1962 CreatePdf(geckoResult
, cbc
);
1964 } // namespace widget
1965 } // namespace mozilla
1967 void nsWindow::InitNatives() {
1968 jni::InitConversionStatics();
1969 mozilla::widget::GeckoViewSupport::Base::Init();
1970 mozilla::widget::LayerViewSupport::Init();
1971 mozilla::widget::NPZCSupport::Init();
1973 mozilla::widget::GeckoEditableSupport::Init();
1974 a11y::SessionAccessibility::Init();
1977 void nsWindow::DetachNatives() {
1978 MOZ_ASSERT(NS_IsMainThread());
1979 mEditableSupport
.Detach();
1980 mNPZCSupport
.Detach();
1981 mLayerViewSupport
.Detach();
1982 mSessionAccessibility
.Detach();
1986 already_AddRefed
<nsWindow
> nsWindow::From(nsPIDOMWindowOuter
* aDOMWindow
) {
1987 nsCOMPtr
<nsIWidget
> widget
= WidgetUtils::DOMWindowToWidget(aDOMWindow
);
1988 return From(widget
);
1992 already_AddRefed
<nsWindow
> nsWindow::From(nsIWidget
* aWidget
) {
1993 // `widget` may be one of several different types in the parent
1994 // process, including the Android nsWindow, PuppetWidget, etc. To
1995 // ensure that the cast to the Android nsWindow is valid, we check that the
1996 // widget is a top-level window and that its NS_NATIVE_WIDGET value is
1997 // non-null, which is not the case for non-native widgets like
1999 if (aWidget
&& aWidget
->GetWindowType() == WindowType::TopLevel
&&
2000 aWidget
->GetNativeData(NS_NATIVE_WIDGET
) == aWidget
) {
2001 RefPtr
<nsWindow
> window
= static_cast<nsWindow
*>(aWidget
);
2002 return window
.forget();
2007 nsWindow
* nsWindow::TopWindow() {
2008 if (!gTopLevelWindows
.IsEmpty()) return gTopLevelWindows
[0];
2012 void nsWindow::LogWindow(nsWindow
* win
, int index
, int indent
) {
2013 #if defined(DEBUG) || defined(FORCE_ALOG)
2014 char spaces
[] = " ";
2015 spaces
[indent
< 20 ? indent
: 20] = 0;
2016 ALOG("%s [% 2d] 0x%p [parent 0x%p] [% 3d,% 3dx% 3d,% 3d] vis %d type %d",
2017 spaces
, index
, win
, win
->mParent
, win
->mBounds
.x
, win
->mBounds
.y
,
2018 win
->mBounds
.width
, win
->mBounds
.height
, win
->mIsVisible
,
2019 int(win
->mWindowType
));
2023 void nsWindow::DumpWindows() { DumpWindows(gTopLevelWindows
); }
2025 void nsWindow::DumpWindows(const nsTArray
<nsWindow
*>& wins
, int indent
) {
2026 for (uint32_t i
= 0; i
< wins
.Length(); ++i
) {
2027 nsWindow
* w
= wins
[i
];
2028 LogWindow(w
, i
, indent
);
2029 DumpWindows(w
->mChildren
, indent
+ 1);
2033 nsWindow::nsWindow()
2034 : mWidgetId(++sWidgetId
),
2037 mDynamicToolbarMaxHeight(0),
2038 mSizeMode(nsSizeMode_Normal
),
2039 mIsFullScreen(false),
2040 mCompositorWidgetDelegate(nullptr),
2041 mDestroyMutex("nsWindow::mDestroyMutex") {}
2043 nsWindow::~nsWindow() {
2044 gTopLevelWindows
.RemoveElement(this);
2045 ALOG("nsWindow %p destructor", (void*)this);
2046 // The mCompositorSession should have been cleaned up in nsWindow::Destroy()
2047 // DestroyLayerManager() will call DestroyCompositor() which will crash if
2048 // called from nsBaseWidget destructor. See Bug 1392705
2049 MOZ_ASSERT(!mCompositorSession
);
2052 bool nsWindow::IsTopLevel() {
2053 return mWindowType
== WindowType::TopLevel
||
2054 mWindowType
== WindowType::Dialog
||
2055 mWindowType
== WindowType::Invisible
;
2058 nsresult
nsWindow::Create(nsIWidget
* aParent
, nsNativeWidget aNativeParent
,
2059 const LayoutDeviceIntRect
& aRect
,
2060 InitData
* aInitData
) {
2061 ALOG("nsWindow[%p]::Create %p [%d %d %d %d]", (void*)this, (void*)aParent
,
2062 aRect
.x
, aRect
.y
, aRect
.width
, aRect
.height
);
2064 nsWindow
* parent
= (nsWindow
*)aParent
;
2065 if (aNativeParent
) {
2068 "Ignoring native parent on Android window [%p], "
2069 "since parent was specified (%p %p)",
2070 (void*)this, (void*)aNativeParent
, (void*)aParent
);
2072 parent
= (nsWindow
*)aNativeParent
;
2076 // A default size of 1x1 confuses MobileViewportManager, so
2077 // use 0x0 instead. This is also a little more fitting since
2078 // we don't yet have a surface yet (and therefore a valid size)
2079 // and 0x0 is usually recognized as invalid.
2080 LayoutDeviceIntRect rect
= aRect
;
2081 if (aRect
.width
== 1 && aRect
.height
== 1) {
2087 SetSizeConstraints(SizeConstraints());
2089 BaseCreate(nullptr, aInitData
);
2091 NS_ASSERTION(IsTopLevel() || parent
,
2092 "non-top-level window doesn't have a parent!");
2095 gTopLevelWindows
.AppendElement(this);
2097 } else if (parent
) {
2098 parent
->mChildren
.AppendElement(this);
2102 #ifdef DEBUG_ANDROID_WIDGET
2109 void nsWindow::Destroy() {
2110 MutexAutoLock
lock(mDestroyMutex
);
2112 nsBaseWidget::mOnDestroyCalled
= true;
2114 // Disassociate our native object from GeckoView.
2115 mGeckoViewSupport
.Detach();
2117 // Stuff below may release the last ref to this
2118 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
2120 while (mChildren
.Length()) {
2121 // why do we still have children?
2122 ALOG("### Warning: Destroying window %p and reparenting child %p to null!",
2123 (void*)this, (void*)mChildren
[0]);
2124 mChildren
[0]->SetParent(nullptr);
2127 // Ensure the compositor has been shutdown before this nsWindow is potentially
2129 nsBaseWidget::DestroyCompositor();
2131 nsBaseWidget::Destroy();
2133 if (IsTopLevel()) gTopLevelWindows
.RemoveElement(this);
2137 nsBaseWidget::OnDestroy();
2139 #ifdef DEBUG_ANDROID_WIDGET
2144 mozilla::widget::EventDispatcher
* nsWindow::GetEventDispatcher() const {
2146 return mAndroidView
->mEventDispatcher
;
2151 void nsWindow::RedrawAll() {
2152 if (mAttachedWidgetListener
) {
2153 mAttachedWidgetListener
->RequestRepaint();
2154 } else if (mWidgetListener
) {
2155 mWidgetListener
->RequestRepaint();
2159 RefPtr
<UiCompositorControllerChild
> nsWindow::GetUiCompositorControllerChild() {
2160 return mCompositorSession
2161 ? mCompositorSession
->GetUiCompositorControllerChild()
2165 mozilla::layers::LayersId
nsWindow::GetRootLayerId() const {
2166 return mCompositorSession
? mCompositorSession
->RootLayerTreeId()
2167 : mozilla::layers::LayersId
{0};
2170 void nsWindow::OnGeckoViewReady() {
2171 auto acc(mGeckoViewSupport
.Access());
2179 void nsWindow::SetParent(nsIWidget
* aNewParent
) {
2180 if ((nsIWidget
*)mParent
== aNewParent
) return;
2182 // If we had a parent before, remove ourselves from its list of
2184 if (mParent
) mParent
->mChildren
.RemoveElement(this);
2186 mParent
= (nsWindow
*)aNewParent
;
2188 if (mParent
) mParent
->mChildren
.AppendElement(this);
2190 // if we are now in the toplevel window's hierarchy, schedule a redraw
2191 if (FindTopLevel() == nsWindow::TopWindow()) RedrawAll();
2194 nsIWidget
* nsWindow::GetParent() { return mParent
; }
2196 RefPtr
<MozPromise
<bool, bool, false>> nsWindow::OnLoadRequest(
2197 nsIURI
* aUri
, int32_t aWindowType
, int32_t aFlags
,
2198 nsIPrincipal
* aTriggeringPrincipal
, bool aHasUserGesture
,
2200 auto geckoViewSupport(mGeckoViewSupport
.Access());
2201 if (!geckoViewSupport
) {
2202 return MozPromise
<bool, bool, false>::CreateAndResolve(false, __func__
);
2205 nsAutoCString spec
, triggeringSpec
;
2207 aUri
->GetDisplaySpec(spec
);
2208 if (aIsTopLevel
&& mozilla::net::SchemeIsData(aUri
) &&
2209 spec
.Length() > MAX_TOPLEVEL_DATA_URI_LEN
) {
2210 return MozPromise
<bool, bool, false>::CreateAndResolve(false, __func__
);
2214 bool isNullPrincipal
= false;
2215 if (aTriggeringPrincipal
) {
2216 aTriggeringPrincipal
->GetIsNullPrincipal(&isNullPrincipal
);
2218 if (!isNullPrincipal
) {
2219 nsCOMPtr
<nsIURI
> triggeringUri
;
2220 BasePrincipal::Cast(aTriggeringPrincipal
)
2221 ->GetURI(getter_AddRefs(triggeringUri
));
2222 if (triggeringUri
) {
2223 triggeringUri
->GetDisplaySpec(triggeringSpec
);
2228 auto geckoResult
= geckoViewSupport
->OnLoadRequest(
2229 spec
.get(), aWindowType
, aFlags
,
2230 isNullPrincipal
? nullptr : triggeringSpec
.get(), aHasUserGesture
,
2233 ? MozPromise
<bool, bool, false>::FromGeckoResult(geckoResult
)
2237 void nsWindow::OnUpdateSessionStore(mozilla::jni::Object::Param aBundle
) {
2238 auto geckoViewSupport(mGeckoViewSupport
.Access());
2239 if (!geckoViewSupport
) {
2243 geckoViewSupport
->OnUpdateSessionStore(aBundle
);
2246 float nsWindow::GetDPI() {
2249 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
2251 screen
->GetDpi(&dpi
);
2257 double nsWindow::GetDefaultScaleInternal() {
2258 double scale
= 1.0f
;
2260 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
2262 screen
->GetContentsScaleFactor(&scale
);
2268 void nsWindow::Show(bool aState
) {
2269 ALOG("nsWindow[%p]::Show %d", (void*)this, aState
);
2271 if (mWindowType
== WindowType::Invisible
) {
2272 ALOG("trying to show invisible window! ignoring..");
2276 if (aState
== mIsVisible
) return;
2278 mIsVisible
= aState
;
2281 // XXX should we bring this to the front when it's shown,
2282 // if it's a toplevel widget?
2284 // XXX we should synthesize a eMouseExitFromWidget (for old top
2285 // window)/eMouseEnterIntoWidget (for new top window) since we need
2286 // to pretend that the top window always has focus. Not sure
2287 // if Show() is the right place to do this, though.
2290 // It just became visible, so bring it to the front.
2293 } else if (nsWindow::TopWindow() == this) {
2294 // find the next visible window to show
2296 for (i
= 1; i
< gTopLevelWindows
.Length(); i
++) {
2297 nsWindow
* win
= gTopLevelWindows
[i
];
2298 if (!win
->mIsVisible
) continue;
2300 win
->BringToFront();
2304 } else if (FindTopLevel() == nsWindow::TopWindow()) {
2308 #ifdef DEBUG_ANDROID_WIDGET
2313 bool nsWindow::IsVisible() const { return mIsVisible
; }
2315 void nsWindow::ConstrainPosition(DesktopIntPoint
& aPoint
) {
2316 ALOG("nsWindow[%p]::ConstrainPosition [%d %d]", (void*)this, aPoint
.x
.value
,
2319 // Constrain toplevel windows; children we don't care about
2321 aPoint
= DesktopIntPoint();
2325 void nsWindow::Move(double aX
, double aY
) {
2326 if (IsTopLevel()) return;
2328 Resize(aX
, aY
, mBounds
.width
, mBounds
.height
, true);
2331 void nsWindow::Resize(double aWidth
, double aHeight
, bool aRepaint
) {
2332 Resize(mBounds
.x
, mBounds
.y
, aWidth
, aHeight
, aRepaint
);
2335 void nsWindow::Resize(double aX
, double aY
, double aWidth
, double aHeight
,
2337 ALOG("nsWindow[%p]::Resize [%f %f %f %f] (repaint %d)", (void*)this, aX
, aY
,
2338 aWidth
, aHeight
, aRepaint
);
2340 LayoutDeviceIntRect oldBounds
= mBounds
;
2342 mBounds
.x
= NSToIntRound(aX
);
2343 mBounds
.y
= NSToIntRound(aY
);
2344 mBounds
.width
= NSToIntRound(aWidth
);
2345 mBounds
.height
= NSToIntRound(aHeight
);
2347 ConstrainSize(&mBounds
.width
, &mBounds
.height
);
2349 bool needPositionDispatch
= mBounds
.TopLeft() != oldBounds
.TopLeft();
2350 bool needSizeDispatch
= mBounds
.Size() != oldBounds
.Size();
2352 if (needSizeDispatch
) {
2353 OnSizeChanged(mBounds
.Size().ToUnknownSize());
2356 if (needPositionDispatch
) {
2357 NotifyWindowMoved(mBounds
.x
, mBounds
.y
);
2360 // Should we skip honoring aRepaint here?
2361 if (aRepaint
&& FindTopLevel() == nsWindow::TopWindow()) RedrawAll();
2364 void nsWindow::SetZIndex(int32_t aZIndex
) {
2365 ALOG("nsWindow[%p]::SetZIndex %d ignored", (void*)this, aZIndex
);
2368 void nsWindow::SetSizeMode(nsSizeMode aMode
) {
2369 if (aMode
== mSizeMode
) {
2376 case nsSizeMode_Minimized
:
2377 java::GeckoAppShell::MoveTaskToBack();
2379 case nsSizeMode_Fullscreen
:
2380 MakeFullScreen(true);
2387 void nsWindow::Enable(bool aState
) {
2388 ALOG("nsWindow[%p]::Enable %d ignored", (void*)this, aState
);
2391 bool nsWindow::IsEnabled() const { return true; }
2393 void nsWindow::Invalidate(const LayoutDeviceIntRect
& aRect
) {}
2395 nsWindow
* nsWindow::FindTopLevel() {
2396 nsWindow
* toplevel
= this;
2398 if (toplevel
->IsTopLevel()) return toplevel
;
2400 toplevel
= toplevel
->mParent
;
2404 "nsWindow::FindTopLevel(): couldn't find a toplevel or dialog window in "
2405 "this [%p] widget's hierarchy!",
2410 void nsWindow::SetFocus(Raise
, mozilla::dom::CallerType aCallerType
) {
2411 FindTopLevel()->BringToFront();
2414 void nsWindow::BringToFront() {
2415 MOZ_ASSERT(XRE_IsParentProcess());
2416 // If the window to be raised is the same as the currently raised one,
2417 // do nothing. We need to check the focus manager as well, as the first
2418 // window that is created will be first in the window list but won't yet
2420 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
2421 if (fm
&& fm
->GetActiveWindow() && FindTopLevel() == nsWindow::TopWindow()) {
2425 if (!IsTopLevel()) {
2426 FindTopLevel()->BringToFront();
2430 RefPtr
<nsWindow
> kungFuDeathGrip(this);
2432 nsWindow
* oldTop
= nullptr;
2433 if (!gTopLevelWindows
.IsEmpty()) {
2434 oldTop
= gTopLevelWindows
[0];
2437 gTopLevelWindows
.RemoveElement(this);
2438 gTopLevelWindows
.InsertElementAt(0, this);
2441 nsIWidgetListener
* listener
= oldTop
->GetWidgetListener();
2443 listener
->WindowDeactivated();
2447 if (mWidgetListener
) {
2448 mWidgetListener
->WindowActivated();
2454 LayoutDeviceIntRect
nsWindow::GetScreenBounds() {
2455 return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds
.Size());
2458 LayoutDeviceIntPoint
nsWindow::WidgetToScreenOffset() {
2459 LayoutDeviceIntPoint
p(0, 0);
2461 for (nsWindow
* w
= this; !!w
; w
= w
->mParent
) {
2462 p
.x
+= w
->mBounds
.x
;
2463 p
.y
+= w
->mBounds
.y
;
2465 if (w
->IsTopLevel()) {
2472 nsresult
nsWindow::DispatchEvent(WidgetGUIEvent
* aEvent
,
2473 nsEventStatus
& aStatus
) {
2474 aStatus
= DispatchEvent(aEvent
);
2478 nsEventStatus
nsWindow::DispatchEvent(WidgetGUIEvent
* aEvent
) {
2479 if (mAttachedWidgetListener
) {
2480 return mAttachedWidgetListener
->HandleEvent(aEvent
, mUseAttachedEvents
);
2481 } else if (mWidgetListener
) {
2482 return mWidgetListener
->HandleEvent(aEvent
, mUseAttachedEvents
);
2484 return nsEventStatus_eIgnore
;
2487 nsresult
nsWindow::MakeFullScreen(bool aFullScreen
) {
2488 if (!mAndroidView
) {
2489 return NS_ERROR_NOT_AVAILABLE
;
2492 mIsFullScreen
= aFullScreen
;
2493 mAndroidView
->mEventDispatcher
->Dispatch(
2494 aFullScreen
? u
"GeckoView:FullScreenEnter" : u
"GeckoView:FullScreenExit");
2496 nsIWidgetListener
* listener
= GetWidgetListener();
2498 mSizeMode
= mIsFullScreen
? nsSizeMode_Fullscreen
: nsSizeMode_Normal
;
2499 listener
->SizeModeChanged(mSizeMode
);
2504 mozilla::WindowRenderer
* nsWindow::GetWindowRenderer() {
2505 if (!mWindowRenderer
) {
2506 CreateLayerManager();
2509 return mWindowRenderer
;
2512 void nsWindow::CreateLayerManager() {
2513 if (mWindowRenderer
) {
2517 nsWindow
* topLevelWindow
= FindTopLevel();
2518 if (!topLevelWindow
|| topLevelWindow
->mWindowType
== WindowType::Invisible
) {
2519 // don't create a layer manager for an invisible top-level window
2523 // Ensure that gfxPlatform is initialized first.
2524 gfxPlatform::GetPlatform();
2526 if (ShouldUseOffMainThreadCompositing()) {
2527 LayoutDeviceIntRect rect
= GetBounds();
2528 CreateCompositor(rect
.Width(), rect
.Height());
2529 if (mWindowRenderer
) {
2530 if (mLayerViewSupport
.IsAttached()) {
2532 "LayerViewSupport::NotifyCompositorCreated",
2533 [lvs
= mLayerViewSupport
,
2534 uiCompositorController
= GetUiCompositorControllerChild()] {
2535 if (auto lvsAccess
{lvs
.Access()}) {
2536 lvsAccess
->NotifyCompositorCreated(uiCompositorController
);
2544 // If we get here, then off main thread compositing failed to initialize.
2545 sFailedToCreateGLContext
= true;
2548 if (!ComputeShouldAccelerate() || sFailedToCreateGLContext
) {
2549 printf_stderr(" -- creating basic, not accelerated\n");
2550 mWindowRenderer
= CreateFallbackRenderer();
2554 void nsWindow::NotifyCompositorSessionLost(
2555 mozilla::layers::CompositorSession
* aSession
) {
2556 nsBaseWidget::NotifyCompositorSessionLost(aSession
);
2558 DispatchToUiThread("nsWindow::NotifyCompositorSessionLost",
2559 [lvs
= mLayerViewSupport
] {
2560 if (auto lvsAccess
{lvs
.Access()}) {
2561 lvsAccess
->NotifyCompositorSessionLost();
2568 void nsWindow::ShowDynamicToolbar() {
2569 auto acc(mGeckoViewSupport
.Access());
2574 acc
->OnShowDynamicToolbar();
2577 void GeckoViewSupport::OnUpdateSessionStore(
2578 mozilla::jni::Object::Param aBundle
) {
2579 GeckoSession::Window::LocalRef
window(mGeckoViewWindow
);
2584 window
->OnUpdateSessionStore(aBundle
);
2587 void nsWindow::OnSizeChanged(const gfx::IntSize
& aSize
) {
2588 ALOG("nsWindow: %p OnSizeChanged [%d %d]", (void*)this, aSize
.width
,
2591 if (mWidgetListener
) {
2592 mWidgetListener
->WindowResized(this, aSize
.width
, aSize
.height
);
2595 if (mAttachedWidgetListener
) {
2596 mAttachedWidgetListener
->WindowResized(this, aSize
.width
, aSize
.height
);
2599 if (mCompositorWidgetDelegate
) {
2600 mCompositorWidgetDelegate
->NotifyClientSizeChanged(
2601 LayoutDeviceIntSize::FromUnknownSize(aSize
));
2605 void nsWindow::InitEvent(WidgetGUIEvent
& event
, LayoutDeviceIntPoint
* aPoint
) {
2607 event
.mRefPoint
= *aPoint
;
2609 event
.mRefPoint
= LayoutDeviceIntPoint(0, 0);
2613 void nsWindow::UpdateOverscrollVelocity(const float aX
, const float aY
) {
2614 if (::mozilla::jni::NativeWeakPtr
<LayerViewSupport
>::Accessor lvs
{
2615 mLayerViewSupport
.Access()}) {
2616 const auto& compositor
= lvs
->GetJavaCompositor();
2617 if (AndroidBridge::IsJavaUiThread()) {
2618 compositor
->UpdateOverscrollVelocity(aX
, aY
);
2623 "nsWindow::UpdateOverscrollVelocity",
2624 [compositor
= GeckoSession::Compositor::GlobalRef(compositor
), aX
, aY
] {
2625 compositor
->UpdateOverscrollVelocity(aX
, aY
);
2630 void nsWindow::UpdateOverscrollOffset(const float aX
, const float aY
) {
2631 if (::mozilla::jni::NativeWeakPtr
<LayerViewSupport
>::Accessor lvs
{
2632 mLayerViewSupport
.Access()}) {
2633 const auto& compositor
= lvs
->GetJavaCompositor();
2634 if (AndroidBridge::IsJavaUiThread()) {
2635 compositor
->UpdateOverscrollOffset(aX
, aY
);
2640 "nsWindow::UpdateOverscrollOffset",
2641 [compositor
= GeckoSession::Compositor::GlobalRef(compositor
), aX
, aY
] {
2642 compositor
->UpdateOverscrollOffset(aX
, aY
);
2647 void* nsWindow::GetNativeData(uint32_t aDataType
) {
2648 switch (aDataType
) {
2649 // used by GLContextProviderEGL, nullptr is EGL_DEFAULT_DISPLAY
2650 case NS_NATIVE_WIDGET
:
2653 case NS_RAW_NATIVE_IME_CONTEXT
: {
2654 void* pseudoIMEContext
= GetPseudoIMEContext();
2655 if (pseudoIMEContext
) {
2656 return pseudoIMEContext
;
2658 // We assume that there is only one context per process on Android
2659 return NS_ONLY_ONE_NATIVE_IME_CONTEXT
;
2662 case NS_JAVA_SURFACE
:
2663 if (::mozilla::jni::NativeWeakPtr
<LayerViewSupport
>::Accessor lvs
{
2664 mLayerViewSupport
.Access()}) {
2665 return lvs
->GetSurface().Get();
2673 void nsWindow::DispatchHitTest(const WidgetTouchEvent
& aEvent
) {
2674 if (aEvent
.mMessage
== eTouchStart
&& aEvent
.mTouches
.Length() == 1) {
2675 // Since touch events don't get retargeted by PositionedEventTargeting.cpp
2676 // code, we dispatch a dummy mouse event that *does* get retargeted.
2677 // Front-end code can use this to activate the highlight element in case
2678 // this touchstart is the start of a tap.
2679 WidgetMouseEvent
hittest(true, eMouseHitTest
, this,
2680 WidgetMouseEvent::eReal
);
2681 hittest
.mRefPoint
= aEvent
.mTouches
[0]->mRefPoint
;
2682 hittest
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
2683 nsEventStatus status
;
2684 DispatchEvent(&hittest
, status
);
2688 void nsWindow::PassExternalResponse(java::WebResponse::Param aResponse
) {
2689 auto acc(mGeckoViewSupport
.Access());
2694 acc
->PassExternalResponse(aResponse
);
2697 mozilla::Modifiers
nsWindow::GetModifiers(int32_t metaState
) {
2698 using mozilla::java::sdk::KeyEvent
;
2699 return (metaState
& KeyEvent::META_ALT_MASK
? MODIFIER_ALT
: 0) |
2700 (metaState
& KeyEvent::META_SHIFT_MASK
? MODIFIER_SHIFT
: 0) |
2701 (metaState
& KeyEvent::META_CTRL_MASK
? MODIFIER_CONTROL
: 0) |
2702 (metaState
& KeyEvent::META_META_MASK
? MODIFIER_META
: 0) |
2703 (metaState
& KeyEvent::META_FUNCTION_ON
? MODIFIER_FN
: 0) |
2704 (metaState
& KeyEvent::META_CAPS_LOCK_ON
? MODIFIER_CAPSLOCK
: 0) |
2705 (metaState
& KeyEvent::META_NUM_LOCK_ON
? MODIFIER_NUMLOCK
: 0) |
2706 (metaState
& KeyEvent::META_SCROLL_LOCK_ON
? MODIFIER_SCROLLLOCK
: 0);
2709 TimeStamp
nsWindow::GetEventTimeStamp(int64_t aEventTime
) {
2710 // Android's event time is SystemClock.uptimeMillis that is counted in ms
2711 // since OS was booted.
2712 // (https://developer.android.com/reference/android/os/SystemClock.html)
2713 // and this SystemClock.uptimeMillis uses SYSTEM_TIME_MONOTONIC.
2714 // Our posix implemententaion of TimeStamp::Now uses SYSTEM_TIME_MONOTONIC
2715 // too. Due to same implementation, we can use this via FromSystemTime.
2717 BaseTimeDurationPlatformUtils::TicksFromMilliseconds(aEventTime
);
2718 return TimeStamp::FromSystemTime(tick
);
2721 void nsWindow::UserActivity() {
2722 if (!mIdleService
) {
2723 mIdleService
= do_GetService("@mozilla.org/widget/useridleservice;1");
2727 mIdleService
->ResetIdleTimeOut(0);
2730 if (FindTopLevel() != nsWindow::TopWindow()) {
2735 RefPtr
<mozilla::a11y::SessionAccessibility
>
2736 nsWindow::GetSessionAccessibility() {
2737 auto acc(mSessionAccessibility
.Access());
2742 return acc
.AsRefPtr();
2745 TextEventDispatcherListener
* nsWindow::GetNativeTextEventDispatcherListener() {
2746 nsWindow
* top
= FindTopLevel();
2749 auto acc(top
->mEditableSupport
.Access());
2751 // Non-GeckoView windows don't support IME operations.
2755 nsCOMPtr
<TextEventDispatcherListener
> ptr
;
2756 if (NS_FAILED(acc
->QueryInterface(NS_GET_IID(TextEventDispatcherListener
),
2757 getter_AddRefs(ptr
)))) {
2764 void nsWindow::SetInputContext(const InputContext
& aContext
,
2765 const InputContextAction
& aAction
) {
2766 nsWindow
* top
= FindTopLevel();
2769 auto acc(top
->mEditableSupport
.Access());
2771 // Non-GeckoView windows don't support IME operations.
2775 // We are using an IME event later to notify Java, and the IME event
2776 // will be processed by the top window. Therefore, to ensure the
2777 // IME event uses the correct mInputContext, we need to let the top
2778 // window process SetInputContext
2779 acc
->SetInputContext(aContext
, aAction
);
2782 InputContext
nsWindow::GetInputContext() {
2783 nsWindow
* top
= FindTopLevel();
2786 auto acc(top
->mEditableSupport
.Access());
2788 // Non-GeckoView windows don't support IME operations.
2789 return InputContext();
2792 // We let the top window process SetInputContext,
2793 // so we should let it process GetInputContext as well.
2794 return acc
->GetInputContext();
2797 nsresult
nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId
,
2798 TouchPointerState aPointerState
,
2799 LayoutDeviceIntPoint aPoint
,
2800 double aPointerPressure
,
2801 uint32_t aPointerOrientation
,
2802 nsIObserver
* aObserver
) {
2803 mozilla::widget::AutoObserverNotifier
notifier(aObserver
, "touchpoint");
2806 switch (aPointerState
) {
2808 // This could be a ACTION_DOWN or ACTION_MOVE depending on the
2809 // existing state; it is mapped to the right thing in Java.
2810 eventType
= java::sdk::MotionEvent::ACTION_POINTER_DOWN
;
2813 // This could be turned into a ACTION_UP in Java
2814 eventType
= java::sdk::MotionEvent::ACTION_POINTER_UP
;
2817 eventType
= java::sdk::MotionEvent::ACTION_CANCEL
;
2819 case TOUCH_HOVER
: // not supported for now
2821 return NS_ERROR_UNEXPECTED
;
2824 MOZ_ASSERT(mNPZCSupport
.IsAttached());
2825 auto npzcSup(mNPZCSupport
.Access());
2826 MOZ_ASSERT(!!npzcSup
);
2828 const auto& npzc
= npzcSup
->GetJavaNPZC();
2829 const auto& bounds
= FindTopLevel()->mBounds
;
2830 aPoint
.x
-= bounds
.x
;
2831 aPoint
.y
-= bounds
.y
;
2834 "nsWindow::SynthesizeNativeTouchPoint",
2835 [npzc
= java::PanZoomController::NativeProvider::GlobalRef(npzc
),
2836 aPointerId
, eventType
, aPoint
, aPointerPressure
, aPointerOrientation
] {
2837 npzc
->SynthesizeNativeTouchPoint(aPointerId
, eventType
, aPoint
.x
,
2838 aPoint
.y
, aPointerPressure
,
2839 aPointerOrientation
);
2844 nsresult
nsWindow::SynthesizeNativeMouseEvent(
2845 LayoutDeviceIntPoint aPoint
, NativeMouseMessage aNativeMessage
,
2846 MouseButton aButton
, nsIWidget::Modifiers aModifierFlags
,
2847 nsIObserver
* aObserver
) {
2848 mozilla::widget::AutoObserverNotifier
notifier(aObserver
, "mouseevent");
2850 MOZ_ASSERT(mNPZCSupport
.IsAttached());
2851 auto npzcSup(mNPZCSupport
.Access());
2852 MOZ_ASSERT(!!npzcSup
);
2854 const auto& npzc
= npzcSup
->GetJavaNPZC();
2855 const auto& bounds
= FindTopLevel()->mBounds
;
2856 aPoint
.x
-= bounds
.x
;
2857 aPoint
.y
-= bounds
.y
;
2859 int32_t nativeMessage
;
2860 switch (aNativeMessage
) {
2861 case NativeMouseMessage::ButtonDown
:
2862 nativeMessage
= java::sdk::MotionEvent::ACTION_POINTER_DOWN
;
2864 case NativeMouseMessage::ButtonUp
:
2865 nativeMessage
= java::sdk::MotionEvent::ACTION_POINTER_UP
;
2867 case NativeMouseMessage::Move
:
2868 nativeMessage
= java::sdk::MotionEvent::ACTION_HOVER_MOVE
;
2871 MOZ_ASSERT_UNREACHABLE("Non supported mouse event on Android");
2872 return NS_ERROR_INVALID_ARG
;
2875 if (aNativeMessage
!= NativeMouseMessage::ButtonUp
) {
2877 case MouseButton::ePrimary
:
2878 button
= java::sdk::MotionEvent::BUTTON_PRIMARY
;
2880 case MouseButton::eMiddle
:
2881 button
= java::sdk::MotionEvent::BUTTON_TERTIARY
;
2883 case MouseButton::eSecondary
:
2884 button
= java::sdk::MotionEvent::BUTTON_SECONDARY
;
2886 case MouseButton::eX1
:
2887 button
= java::sdk::MotionEvent::BUTTON_BACK
;
2889 case MouseButton::eX2
:
2890 button
= java::sdk::MotionEvent::BUTTON_FORWARD
;
2893 if (aNativeMessage
== NativeMouseMessage::ButtonDown
) {
2894 MOZ_ASSERT_UNREACHABLE("Non supported mouse button type on Android");
2895 return NS_ERROR_INVALID_ARG
;
2901 // TODO (bug 1693237): Handle aModifierFlags.
2903 "nsWindow::SynthesizeNativeMouseEvent",
2904 [npzc
= java::PanZoomController::NativeProvider::GlobalRef(npzc
),
2905 nativeMessage
, aPoint
, button
] {
2906 npzc
->SynthesizeNativeMouseEvent(nativeMessage
, aPoint
.x
, aPoint
.y
,
2912 nsresult
nsWindow::SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint
,
2913 nsIObserver
* aObserver
) {
2914 return SynthesizeNativeMouseEvent(
2915 aPoint
, NativeMouseMessage::Move
, MouseButton::eNotPressed
,
2916 nsIWidget::Modifiers::NO_MODIFIERS
, aObserver
);
2919 void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate
* delegate
) {
2921 mCompositorWidgetDelegate
= delegate
->AsPlatformSpecificDelegate();
2922 MOZ_ASSERT(mCompositorWidgetDelegate
,
2923 "nsWindow::SetCompositorWidgetDelegate called with a "
2924 "non-PlatformCompositorWidgetDelegate");
2926 mCompositorWidgetDelegate
= nullptr;
2930 void nsWindow::GetCompositorWidgetInitData(
2931 mozilla::widget::CompositorWidgetInitData
* aInitData
) {
2932 *aInitData
= mozilla::widget::AndroidCompositorWidgetInitData(
2933 mWidgetId
, GetClientSize());
2936 bool nsWindow::WidgetPaintsBackground() {
2937 return StaticPrefs::android_widget_paints_background();
2940 bool nsWindow::NeedsPaint() {
2941 auto lvs(mLayerViewSupport
.Access());
2942 if (!lvs
|| lvs
->CompositorPaused() || !GetWindowRenderer()) {
2946 return nsIWidget::NeedsPaint();
2949 void nsWindow::ConfigureAPZControllerThread() {
2950 nsCOMPtr
<nsISerialEventTarget
> thread
= mozilla::GetAndroidUiThread();
2951 APZThreadUtils::SetControllerThread(thread
);
2954 already_AddRefed
<GeckoContentController
>
2955 nsWindow::CreateRootContentController() {
2956 RefPtr
<GeckoContentController
> controller
=
2957 new AndroidContentController(this, mAPZEventState
, mAPZC
);
2958 return controller
.forget();
2961 uint32_t nsWindow::GetMaxTouchPoints() const {
2962 return java::GeckoAppShell::GetMaxTouchPoints();
2965 void nsWindow::UpdateZoomConstraints(
2966 const uint32_t& aPresShellId
, const ScrollableLayerGuid::ViewID
& aViewId
,
2967 const mozilla::Maybe
<ZoomConstraints
>& aConstraints
) {
2968 nsBaseWidget::UpdateZoomConstraints(aPresShellId
, aViewId
, aConstraints
);
2971 CompositorBridgeChild
* nsWindow::GetCompositorBridgeChild() const {
2972 return mCompositorSession
? mCompositorSession
->GetCompositorBridgeChild()
2976 void nsWindow::SetContentDocumentDisplayed(bool aDisplayed
) {
2977 mContentDocumentDisplayed
= aDisplayed
;
2980 bool nsWindow::IsContentDocumentDisplayed() {
2981 return mContentDocumentDisplayed
;
2984 void nsWindow::RecvToolbarAnimatorMessageFromCompositor(int32_t aMessage
) {
2985 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
2986 if (::mozilla::jni::NativeWeakPtr
<LayerViewSupport
>::Accessor lvs
{
2987 mLayerViewSupport
.Access()}) {
2988 lvs
->RecvToolbarAnimatorMessage(aMessage
);
2992 void nsWindow::UpdateRootFrameMetrics(const ScreenPoint
& aScrollOffset
,
2993 const CSSToScreenScale
& aZoom
) {
2994 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
2995 if (::mozilla::jni::NativeWeakPtr
<LayerViewSupport
>::Accessor lvs
{
2996 mLayerViewSupport
.Access()}) {
2997 const auto& compositor
= lvs
->GetJavaCompositor();
2998 mContentDocumentDisplayed
= true;
2999 compositor
->UpdateRootFrameMetrics(aScrollOffset
.x
, aScrollOffset
.y
,
3004 void nsWindow::RecvScreenPixels(Shmem
&& aMem
, const ScreenIntSize
& aSize
,
3006 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
3007 if (::mozilla::jni::NativeWeakPtr
<LayerViewSupport
>::Accessor lvs
{
3008 mLayerViewSupport
.Access()}) {
3009 lvs
->RecvScreenPixels(std::move(aMem
), aSize
, aNeedsYFlip
);
3013 void nsWindow::UpdateDynamicToolbarMaxHeight(ScreenIntCoord aHeight
) {
3014 if (mDynamicToolbarMaxHeight
== aHeight
) {
3018 mDynamicToolbarMaxHeight
= aHeight
;
3020 if (mWidgetListener
) {
3021 mWidgetListener
->DynamicToolbarMaxHeightChanged(aHeight
);
3024 if (mAttachedWidgetListener
) {
3025 mAttachedWidgetListener
->DynamicToolbarMaxHeightChanged(aHeight
);
3029 void nsWindow::UpdateDynamicToolbarOffset(ScreenIntCoord aOffset
) {
3030 if (mWidgetListener
) {
3031 mWidgetListener
->DynamicToolbarOffsetChanged(aOffset
);
3034 if (mAttachedWidgetListener
) {
3035 mAttachedWidgetListener
->DynamicToolbarOffsetChanged(aOffset
);
3039 ScreenIntMargin
nsWindow::GetSafeAreaInsets() const { return mSafeAreaInsets
; }
3041 void nsWindow::UpdateSafeAreaInsets(const ScreenIntMargin
& aSafeAreaInsets
) {
3042 mSafeAreaInsets
= aSafeAreaInsets
;
3044 if (mWidgetListener
) {
3045 mWidgetListener
->SafeAreaInsetsChanged(aSafeAreaInsets
);
3048 if (mAttachedWidgetListener
) {
3049 mAttachedWidgetListener
->SafeAreaInsetsChanged(aSafeAreaInsets
);
3053 jni::NativeWeakPtr
<NPZCSupport
> nsWindow::GetNPZCSupportWeakPtr() {
3054 return mNPZCSupport
;
3057 already_AddRefed
<nsIWidget
> nsIWidget::CreateTopLevelWindow() {
3058 nsCOMPtr
<nsIWidget
> window
= new nsWindow();
3059 return window
.forget();
3062 already_AddRefed
<nsIWidget
> nsIWidget::CreateChildWindow() {
3063 nsCOMPtr
<nsIWidget
> window
= new nsWindow();
3064 return window
.forget();
3067 static already_AddRefed
<DataSourceSurface
> GetCursorImage(
3068 const nsIWidget::Cursor
& aCursor
, mozilla::CSSToLayoutDeviceScale aScale
) {
3069 if (!aCursor
.IsCustom()) {
3073 RefPtr
<DataSourceSurface
> destDataSurface
;
3075 nsIntSize size
= nsIWidget::CustomCursorSize(aCursor
);
3076 // prevent DoS attacks
3077 if (size
.width
> 128 || size
.height
> 128) {
3081 RefPtr
<gfx::SourceSurface
> surface
= aCursor
.mContainer
->GetFrameAtSize(
3082 size
* aScale
.scale
, imgIContainer::FRAME_CURRENT
,
3083 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
3084 if (NS_WARN_IF(!surface
)) {
3088 RefPtr
<DataSourceSurface
> srcDataSurface
= surface
->GetDataSurface();
3089 if (NS_WARN_IF(!srcDataSurface
)) {
3093 DataSourceSurface::ScopedMap
sourceMap(srcDataSurface
,
3094 DataSourceSurface::READ
);
3096 destDataSurface
= gfx::Factory::CreateDataSourceSurfaceWithStride(
3097 srcDataSurface
->GetSize(), SurfaceFormat::R8G8B8A8
,
3098 sourceMap
.GetStride());
3099 if (NS_WARN_IF(!destDataSurface
)) {
3103 DataSourceSurface::ScopedMap
destMap(destDataSurface
,
3104 DataSourceSurface::READ_WRITE
);
3106 SwizzleData(sourceMap
.GetData(), sourceMap
.GetStride(), surface
->GetFormat(),
3107 destMap
.GetData(), destMap
.GetStride(), SurfaceFormat::R8G8B8A8
,
3108 destDataSurface
->GetSize());
3110 return destDataSurface
.forget();
3113 static int32_t GetCursorType(nsCursor aCursor
) {
3114 // When our minimal requirement of SDK version is 25+,
3115 // we should replace with JNI auto-generator.
3117 case eCursor_standard
:
3118 // android.view.PointerIcon.TYPE_ARROW
3121 // android.view.PointerIcon.TYPE_WAIT
3123 case eCursor_select
:
3124 // android.view.PointerIcon.TYPE_TEXT;
3126 case eCursor_hyperlink
:
3127 // android.view.PointerIcon.TYPE_HAND
3129 case eCursor_n_resize
:
3130 case eCursor_s_resize
:
3131 case eCursor_ns_resize
:
3132 case eCursor_row_resize
:
3133 // android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW
3135 case eCursor_w_resize
:
3136 case eCursor_e_resize
:
3137 case eCursor_ew_resize
:
3138 case eCursor_col_resize
:
3139 // android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW
3141 case eCursor_nw_resize
:
3142 case eCursor_se_resize
:
3143 case eCursor_nwse_resize
:
3144 // android.view.PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW
3146 case eCursor_ne_resize
:
3147 case eCursor_sw_resize
:
3148 case eCursor_nesw_resize
:
3149 // android.view.PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW
3151 case eCursor_crosshair
:
3152 // android.view.PointerIcon.TYPE_CROSSHAIR
3155 // android.view.PointerIcon.TYPE_ARROW
3158 // android.view.PointerIcon.TYPE_HELP
3161 // android.view.PointerIcon.TYPE_COPY
3164 // android.view.PointerIcon.TYPE_ALIAS
3166 case eCursor_context_menu
:
3167 // android.view.PointerIcon.TYPE_CONTEXT_MENU
3170 // android.view.PointerIcon.TYPE_CELL
3173 // android.view.PointerIcon.TYPE_GRAB
3175 case eCursor_grabbing
:
3176 // android.view.PointerIcon.TYPE_GRABBING
3178 case eCursor_spinning
:
3179 // android.view.PointerIcon.TYPE_WAIT
3181 case eCursor_zoom_in
:
3182 // android.view.PointerIcon.TYPE_ZOOM_IN
3184 case eCursor_zoom_out
:
3185 // android.view.PointerIcon.TYPE_ZOOM_OUT
3187 case eCursor_not_allowed
:
3188 // android.view.PointerIcon.TYPE_NO_DROP:
3190 case eCursor_no_drop
:
3191 // android.view.PointerIcon.TYPE_NO_DROP:
3193 case eCursor_vertical_text
:
3194 // android.view.PointerIcon.TYPE_VERTICAL_TEXT
3196 case eCursor_all_scroll
:
3197 // android.view.PointerIcon.TYPE_ALL_SCROLL
3200 // android.view.PointerIcon.TYPE_NULL
3203 NS_WARNING_ASSERTION(aCursor
, "Invalid cursor type");
3204 // android.view.PointerIcon.TYPE_ARROW
3209 void nsWindow::SetCursor(const Cursor
& aCursor
) {
3210 if (mozilla::jni::GetAPIVersion() < 24) {
3214 // Only change cursor if it's actually been changed
3215 if (!mUpdateCursor
&& mCursor
== aCursor
) {
3219 mUpdateCursor
= false;
3223 RefPtr
<DataSourceSurface
> destDataSurface
=
3224 GetCursorImage(aCursor
, GetDefaultScale());
3225 if (!destDataSurface
) {
3226 type
= GetCursorType(aCursor
.mDefaultCursor
);
3229 if (mozilla::jni::NativeWeakPtr
<LayerViewSupport
>::Accessor lvs
{
3230 mLayerViewSupport
.Access()}) {
3231 const auto& compositor
= lvs
->GetJavaCompositor();
3234 "nsWindow::SetCursor",
3235 [compositor
= GeckoSession::Compositor::GlobalRef(compositor
), type
,
3236 destDataSurface
= std::move(destDataSurface
),
3237 hotspotX
= aCursor
.mHotspotX
, hotspotY
= aCursor
.mHotspotY
] {
3238 java::sdk::Bitmap::LocalRef bitmap
;
3239 if (destDataSurface
) {
3240 DataSourceSurface::ScopedMap
destMap(destDataSurface
,
3241 DataSourceSurface::READ
);
3242 auto pixels
= mozilla::jni::ByteBuffer::New(
3243 reinterpret_cast<int8_t*>(destMap
.GetData()),
3244 destMap
.GetStride() * destDataSurface
->GetSize().height
);
3245 bitmap
= java::sdk::Bitmap::CreateBitmap(
3246 destDataSurface
->GetSize().width
,
3247 destDataSurface
->GetSize().height
,
3248 java::sdk::Bitmap::Config::ARGB_8888());
3249 bitmap
->CopyPixelsFromBuffer(pixels
);
3251 compositor
->SetPointerIcon(type
, bitmap
, hotspotX
, hotspotY
);