Backed out changeset f1426851ae30 (bug 1844755) for causing failures on test_printpre...
[gecko.git] / widget / android / nsWindow.cpp
blobea34246c4bcf905ec065daac267f74c774dc260e
1 /* -*- Mode: c++; c-basic-offset: 2; tab-width: 4; indent-tabs-mode: nil; -*-
2 * vim: set sw=2 ts=4 expandtab:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include <algorithm>
8 #include <atomic>
9 #include <android/log.h>
10 #include <android/native_window.h>
11 #include <android/native_window_jni.h>
12 #include <math.h>
13 #include <queue>
14 #include <type_traits>
15 #include <unistd.h>
17 #include "AndroidGraphics.h"
18 #include "AndroidBridge.h"
19 #include "AndroidBridgeUtilities.h"
20 #include "AndroidCompositorWidget.h"
21 #include "AndroidContentController.h"
22 #include "AndroidUiThread.h"
23 #include "AndroidView.h"
24 #include "gfxContext.h"
25 #include "GeckoEditableSupport.h"
26 #include "GeckoViewOutputStream.h"
27 #include "GeckoViewSupport.h"
28 #include "GLContext.h"
29 #include "GLContextProvider.h"
30 #include "JavaBuiltins.h"
31 #include "JavaExceptions.h"
32 #include "KeyEvent.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"
50 #include "nsString.h"
51 #include "nsTArray.h"
52 #include "nsThreadUtils.h"
53 #include "nsUserIdleService.h"
54 #include "nsViewManager.h"
55 #include "nsWidgetsCID.h"
56 #include "nsWindow.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
130 // one.
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};
156 namespace {
157 template <class Instance, class Impl>
158 std::enable_if_t<jni::detail::NativePtrPicker<Impl>::value ==
159 jni::detail::NativePtrType::REFPTR,
160 void>
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,
168 void>
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)));
177 return true;
179 return false;
181 } // namespace
183 namespace mozilla {
184 namespace widget {
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> {
194 WindowPtr mWindow;
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;
213 Lambda mLambda;
215 public:
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();
228 return;
231 auto acc = npzcSupportWeak->Access();
232 if (!acc) {
233 // We already shut down.
234 env->ExceptionClear();
235 return;
238 auto win = acc->mWindow.Access();
239 if (!win) {
240 // We already shut down.
241 env->ExceptionClear();
242 return;
245 nsWindow* const window = win->GetNsWindow();
246 if (!window) {
247 // We already shut down.
248 env->ExceptionClear();
249 return;
252 window->UserActivity();
253 return mLambda(window);
256 bool IsUIEvent() const override { return true; }
259 class MOZ_HEAP_CLASS Observer final : public AndroidVsync::Observer {
260 public:
261 static Observer* Create(jni::NativeWeakPtr<NPZCSupport>&& aNPZCSupport) {
262 return new Observer(std::move(aNPZCSupport));
265 private:
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();
274 if (!accessor) {
275 return;
278 accessor->mTouchResampler.NotifyFrame(
279 aTimeStamp -
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)));
298 public:
299 typedef java::PanZoomController::NativeProvider::Natives<NPZCSupport> Base;
301 NPZCSupport(WindowPtr aWindow,
302 const java::PanZoomController::NativeProvider::LocalRef& aNPZC)
303 : mWindow(aWindow), mNPZC(aNPZC) {
304 #if defined(DEBUG)
305 auto win(mWindow.Access());
306 MOZ_ASSERT(!!win);
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();
316 ~NPZCSupport() {
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
338 // thread.
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);
362 if (!npzc) {
363 return;
366 uiThread->Dispatch(
367 NS_NewRunnableFunction("NPZCSupport::OnWeakNonIntrusiveDetach",
368 [npzc, disposer = std::move(disposer)] {
369 npzc->SetAttached(false);
370 disposer->Run();
371 }));
375 const java::PanZoomController::NativeProvider::Ref& GetJavaNPZC() const {
376 return mNPZC;
379 public:
380 void SetIsLongpressEnabled(bool aIsLongpressEnabled) {
381 RefPtr<IAPZCTreeManager> controller;
383 if (auto window = mWindow.Access()) {
384 nsWindow* gkWindow = window->GetNsWindow();
385 if (gkWindow) {
386 controller = gkWindow->mAPZC;
390 if (controller) {
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();
403 if (gkWindow) {
404 controller = gkWindow->mAPZC;
408 if (!controller) {
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
427 // to do?
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;
447 default:
448 MOZ_ASSERT_UNREACHABLE("Unexpected nsEventStatus");
449 return INPUT_RESULT_UNHANDLED;
453 private:
454 static MouseInput::ButtonType GetButtonType(int button) {
455 MouseInput::ButtonType result = MouseInput::NONE;
457 switch (button) {
458 case java::sdk::MotionEvent::BUTTON_PRIMARY:
459 result = MouseInput::PRIMARY_BUTTON;
460 break;
461 case java::sdk::MotionEvent::BUTTON_SECONDARY:
462 result = MouseInput::SECONDARY_BUTTON;
463 break;
464 case java::sdk::MotionEvent::BUTTON_TERTIARY:
465 result = MouseInput::MIDDLE_BUTTON;
466 break;
467 default:
468 break;
471 return result;
474 static int16_t ConvertButtons(int buttons) {
475 int16_t result = 0;
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;
493 return result;
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;
526 return ret;
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;
538 return ret;
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));
549 public:
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();
558 if (gkWindow) {
559 controller = gkWindow->mAPZC;
563 if (!controller) {
564 return INPUT_RESULT_UNHANDLED;
567 MouseInput::MouseType mouseType = MouseInput::MOUSE_NONE;
568 MouseInput::ButtonType buttonType = MouseInput::NONE;
569 switch (aAction) {
570 case java::sdk::MotionEvent::ACTION_DOWN:
571 mouseType = MouseInput::MOUSE_DOWN;
572 buttonType = GetButtonType(buttons ^ mPreviousButtons);
573 mPreviousButtons = buttons;
574 break;
575 case java::sdk::MotionEvent::ACTION_UP:
576 mouseType = MouseInput::MOUSE_UP;
577 buttonType = GetButtonType(buttons ^ mPreviousButtons);
578 mPreviousButtons = buttons;
579 break;
580 case java::sdk::MotionEvent::ACTION_MOVE:
581 mouseType = MouseInput::MOUSE_MOVE;
582 break;
583 case java::sdk::MotionEvent::ACTION_HOVER_MOVE:
584 mouseType = MouseInput::MOUSE_MOVE;
585 break;
586 case java::sdk::MotionEvent::ACTION_HOVER_ENTER:
587 mouseType = MouseInput::MOUSE_WIDGET_ENTER;
588 break;
589 case java::sdk::MotionEvent::ACTION_HOVER_EXIT:
590 mouseType = MouseInput::MOUSE_WIDGET_EXIT;
591 break;
592 default:
593 break;
596 if (mouseType == MouseInput::MOUSE_NONE) {
597 return INPUT_RESULT_UNHANDLED;
600 ScreenPoint origin = ScreenPoint(aX, aY);
602 MouseInput input(
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;
624 default:
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
639 if (angle >= 90.0) {
640 angle -= 180.0f;
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.
650 ScreenSize radius;
651 if (angle < 0.0f) {
652 angle += 90.0f;
653 radius =
654 ScreenSize(int32_t(aToolMajor / 2.0f), int32_t(aToolMinor / 2.0f));
655 } else {
656 radius =
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);
670 auto eventData =
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;
682 break;
683 case java::sdk::MotionEvent::ACTION_MOVE:
684 type = MultiTouchInput::MULTITOUCH_MOVE;
685 break;
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;
693 break;
694 case java::sdk::MotionEvent::ACTION_OUTSIDE:
695 case java::sdk::MotionEvent::ACTION_CANCEL:
696 type = MultiTouchInput::MULTITOUCH_CANCEL;
697 break;
698 default:
699 if (returnResult) {
700 returnResult->Complete(
701 java::sdk::Integer::ValueOf(INPUT_RESULT_UNHANDLED));
703 return;
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,
759 pressure[i]);
761 for (size_t historyIndex = 0; historyIndex < historySize;
762 historyIndex++) {
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]),
773 historicalPoint,
774 {}, // mLocalScreenPoint will be computed later by APZ
775 historicalRadius,
776 historicalAngle,
777 historicalPressure[historicalI]});
780 input.mTouches.AppendElement(singleTouchData);
783 if (mAndroidVsync &&
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));
793 return;
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();
809 if (!win) {
810 return;
812 RefPtr<nsWindow> gkWindow = win->GetNsWindow();
813 if (!gkWindow) {
814 return;
816 MutexAutoLock lock(gkWindow->GetDestroyMutex());
817 if (gkWindow->Destroyed()) {
818 return;
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);
826 mObserver = nullptr;
828 mListeningToVsync = aNeedVsync;
831 void ConsumeMotionEventsFromResampler() {
832 auto outgoing = mTouchResampler.ConsumeOutgoingEvents();
833 while (!outgoing.empty()) {
834 auto outgoingEvent = std::move(outgoing.front());
835 outgoing.pop();
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();
858 if (gkWindow) {
859 controller = gkWindow->mAPZC;
863 if (!controller) {
864 if (aReturnResult) {
865 aReturnResult->Complete(java::PanZoomController::InputResultDetail::New(
866 INPUT_RESULT_UNHANDLED,
867 java::PanZoomController::SCROLLABLE_FLAG_NONE,
868 java::PanZoomController::OVERSCROLL_FLAG_NONE));
870 return;
873 APZInputBridge::InputBlockCallback callback;
874 if (aReturnResult) {
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) {
885 if (aReturnResult) {
886 if (result.GetHandledResult() != Nothing()) {
887 aReturnResult->Complete(
888 ConvertAPZHandledResult(result.GetHandledResult().value()));
889 } else {
890 MOZ_ASSERT_UNREACHABLE(
891 "nsEventStatus_eConsumeNoDefault should involve a valid "
892 "APZHandledResult");
893 aReturnResult->Complete(
894 java::PanZoomController::InputResultDetail::New(
895 INPUT_RESULT_IGNORED,
896 java::PanZoomController::SCROLLABLE_FLAG_NONE,
897 java::PanZoomController::OVERSCROLL_FLAG_NONE));
900 return;
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) {
923 if (!mInitData) {
924 aOut.setNull();
925 return NS_OK;
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> {
937 WindowPtr mWindow;
938 GeckoSession::Compositor::WeakRef mCompositor;
939 Atomic<bool, ReleaseAcquire> mCompositorPaused;
940 java::sdk::Surface::GlobalRef mSurface;
941 java::sdk::SurfaceControl::GlobalRef mSurfaceControl;
942 int32_t mX;
943 int32_t mY;
944 int32_t mWidth;
945 int32_t mHeight;
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)
961 : mResult(aResult),
962 mBitmap(aBitmap),
963 mSource(aSource),
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;
972 ScreenRect mSource;
974 IntSize mOutputSize;
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;
984 public:
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();
999 if (event) {
1000 event->setPrevious(this);
1001 } else {
1002 queue.insertBack(this);
1007 public:
1008 typedef GeckoSession::Compositor::Natives<LayerViewSupport> Base;
1010 LayerViewSupport(WindowPtr aWindow,
1011 const GeckoSession::Compositor::LocalRef& aInstance)
1012 : mWindow(aWindow), mCompositor(aInstance), mCompositorPaused(true) {
1013 #if defined(DEBUG)
1014 auto win(mWindow.Access());
1015 MOZ_ASSERT(!!win);
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);
1028 if (!compositor) {
1029 return;
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()) {
1038 auto aResult =
1039 java::GeckoResult::LocalRef(results->front().mResult);
1040 if (aResult) {
1041 aResult->CompleteExceptionally(
1042 java::sdk::IllegalStateException::New(
1043 "The compositor has detached from the session")
1044 .Cast<jni::Throwable>());
1046 results->pop();
1050 compositor->OnCompositorDetached();
1051 disposer->Run();
1052 }));
1056 const GeckoSession::Compositor::Ref& GetJavaCompositor() const {
1057 return mCompositor;
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();
1084 if (gkWindow) {
1085 mUiCompositorControllerChild->OnCompositorSurfaceChanged(
1086 gkWindow->mWidgetId, mSurface);
1090 bool resumed = mUiCompositorControllerChild->ResumeAndResize(
1091 mX, mY, mWidth, mHeight);
1092 if (!resumed) {
1093 gfxCriticalNote
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
1108 // created.
1109 mSurface = nullptr;
1112 if (auto window = mWindow.Access()) {
1113 while (!mCapturePixelsResults.empty()) {
1114 auto result =
1115 java::GeckoResult::LocalRef(mCapturePixelsResults.front().mResult);
1116 if (result) {
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; }
1129 private:
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);
1141 if (!drawTarget) {
1142 return nullptr;
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
1162 public:
1163 void AttachNPZC(jni::Object::Param aNPZC) {
1164 MOZ_ASSERT(NS_IsMainThread());
1165 MOZ_ASSERT(aNPZC);
1167 auto locked(mWindow.Access());
1168 if (!locked) {
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
1177 // well.
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);
1186 DispatchToUiThread(
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,
1194 int32_t aHeight) {
1195 MOZ_ASSERT(NS_IsMainThread());
1196 auto acc = mWindow.Access();
1197 if (!acc) {
1198 return; // Already shut down.
1201 nsWindow* gkWindow = acc->GetNsWindow();
1202 if (!gkWindow) {
1203 return;
1206 gkWindow->Resize(aLeft, aTop, aWidth, aHeight, /* repaint */ false);
1209 void NotifyMemoryPressure() {
1210 MOZ_ASSERT(NS_IsMainThread());
1211 auto acc = mWindow.Access();
1212 if (!acc) {
1213 return; // Already shut down.
1216 nsWindow* gkWindow = acc->GetNsWindow();
1217 if (!gkWindow || !gkWindow->mCompositorBridgeChild) {
1218 return;
1221 gkWindow->mCompositorBridgeChild->SendNotifyMemoryPressure();
1224 void SetDynamicToolbarMaxHeight(int32_t aHeight) {
1225 MOZ_ASSERT(NS_IsMainThread());
1226 auto acc = mWindow.Access();
1227 if (!acc) {
1228 return; // Already shut down.
1231 nsWindow* gkWindow = acc->GetNsWindow();
1232 if (!gkWindow) {
1233 return;
1236 gkWindow->UpdateDynamicToolbarMaxHeight(ScreenIntCoord(aHeight));
1239 void SyncPauseCompositor() {
1240 MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
1242 mCompositorPaused = true;
1244 if (mUiCompositorControllerChild) {
1245 mUiCompositorControllerChild->Pause();
1247 mSurface = nullptr;
1248 mSurfaceControl = nullptr;
1249 if (auto window = mWindow.Access()) {
1250 nsWindow* gkWindow = window->GetNsWindow();
1251 if (gkWindow) {
1252 mUiCompositorControllerChild->OnCompositorSurfaceChanged(
1253 gkWindow->mWidgetId, nullptr);
1258 if (auto lock{mWindow.Access()}) {
1259 while (!mCapturePixelsResults.empty()) {
1260 auto result =
1261 java::GeckoResult::LocalRef(mCapturePixelsResults.front().mResult);
1262 if (result) {
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();
1279 if (!resumed) {
1280 gfxCriticalNote
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());
1293 mX = aX;
1294 mY = aY;
1295 mWidth = aWidth;
1296 mHeight = aHeight;
1297 mSurfaceControl =
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);
1306 } else {
1307 mSurface = java::sdk::Surface::GlobalRef::From(aSurface);
1310 if (mUiCompositorControllerChild) {
1311 if (auto window = mWindow.Access()) {
1312 nsWindow* gkWindow = window->GetNsWindow();
1313 if (gkWindow) {
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);
1322 if (!resumed) {
1323 gfxCriticalNote
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
1327 // infinite loop.
1328 if (!mRequestedNewSurface) {
1329 RequestNewSurface();
1331 return;
1335 mRequestedNewSurface = false;
1337 mCompositorPaused = false;
1339 class OnResumedEvent : public nsAppShell::Event {
1340 GeckoSession::Compositor::GlobalRef mCompositor;
1342 public:
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));
1353 if (!lvsHolder) {
1354 env->ExceptionClear();
1355 return; // Already shut down.
1358 auto lvs(lvsHolder->Access());
1359 if (!lvs) {
1360 env->ExceptionClear();
1361 return; // Already shut down.
1364 auto win = lvs->mWindow.Access();
1365 if (!win) {
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();
1377 if (gkWindow) {
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(
1394 mSurfaceControl);
1396 compositor->RequestNewSurface();
1400 mozilla::jni::Object::LocalRef GetMagnifiableSurface() {
1401 return mozilla::jni::Object::LocalRef::From(GetSurface());
1404 void SyncInvalidateAndScheduleComposite() {
1405 if (!mUiCompositorControllerChild) {
1406 return;
1409 if (AndroidBridge::IsJavaUiThread()) {
1410 mUiCompositorControllerChild->InvalidateAndRender();
1411 return;
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();
1434 if (gkWindow) {
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);
1445 }));
1449 void SendToolbarAnimatorMessage(int32_t aMessage) {
1450 if (!mUiCompositorControllerChild) {
1451 return;
1454 if (AndroidBridge::IsJavaUiThread()) {
1455 mUiCompositorControllerChild->ToolbarAnimatorMessageFromUI(aMessage);
1456 return;
1459 if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
1460 uiThread->Dispatch(
1461 NewRunnableMethod<int32_t>(
1462 "LayerViewSupport::ToolbarAnimatorMessageFromUI",
1463 mUiCompositorControllerChild,
1464 &UiCompositorControllerChild::ToolbarAnimatorMessageFromUI,
1465 aMessage),
1466 nsIThread::DISPATCH_NORMAL);
1470 void RecvToolbarAnimatorMessage(int32_t aMessage) {
1471 auto compositor = GeckoSession::Compositor::LocalRef(mCompositor);
1472 if (compositor) {
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) {
1494 if (result) {
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>());
1502 return;
1505 int size = 0;
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();
1515 if (size == 1) {
1516 mUiCompositorControllerChild->RequestScreenPixels();
1520 void RecvScreenPixels(Shmem&& aMem, const ScreenIntSize& aSize,
1521 bool aNeedsYFlip) {
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();
1537 if (result) {
1538 if (bitmap) {
1539 RefPtr<DataSourceSurface> surf;
1540 if (aNeedsYFlip) {
1541 surf = FlipScreenPixels(aMem, aSize, request.mSource,
1542 request.mOutputSize);
1543 } else {
1544 surf = gfx::Factory::CreateWrappingDataSourceSurface(
1545 aMem.get<uint8_t>(),
1546 StrideForFormatAndWidth(SurfaceFormat::B8G8R8A8, aSize.width),
1547 IntSize(aSize.width, aSize.height), SurfaceFormat::B8G8R8A8);
1549 if (surf) {
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);
1556 } else {
1557 result->CompleteExceptionally(
1558 java::sdk::IllegalStateException::New(
1559 "Failed to create flipped snapshot surface (probably out "
1560 "of memory)")
1561 .Cast<jni::Throwable>());
1563 } else {
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,
1590 int32_t aLeft) {
1591 MOZ_ASSERT(NS_IsMainThread());
1592 auto win(mWindow.Access());
1593 if (!win) {
1594 return; // Already shut down.
1597 nsWindow* gkWindow = win->GetNsWindow();
1598 if (!gkWindow) {
1599 return;
1602 ScreenIntMargin safeAreaInsets(aTop, aRight, aBottom, aLeft);
1603 gkWindow->UpdateSafeAreaInsets(safeAreaInsets);
1607 GeckoViewSupport::~GeckoViewSupport() {
1608 if (mWindow) {
1609 mWindow->DetachNatives();
1613 /* static */
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
1626 // start.
1627 gfxPlatform::GetPlatform();
1629 nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID);
1630 MOZ_RELEASE_ASSERT(ww);
1632 nsAutoCString url;
1633 if (aChromeURI) {
1634 url = aChromeURI->ToCString();
1635 } else {
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");
1649 if (aPrivateMode) {
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);
1659 MOZ_ASSERT(window);
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());
1681 if (appWindow) {
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() {
1690 if (mWindow) {
1691 if (mWindow->mAndroidView) {
1692 mWindow->mAndroidView->mEventDispatcher->Detach();
1694 mWindow = nullptr;
1697 if (!mDOMWindow) {
1698 return;
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));
1717 bool attachLvs;
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;
1725 if (attachLvs) {
1726 mWindow->mLayerViewSupport =
1727 jni::NativeWeakPtrHolder<LayerViewSupport>::Attach(
1728 compositor, mWindow->mGeckoViewSupport, compositor);
1730 if (RefPtr<UiCompositorControllerChild> uiCompositorController =
1731 mWindow->GetUiCompositorControllerChild()) {
1732 DispatchToUiThread(
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);
1752 promise->Then(
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()) {
1761 return;
1764 MOZ_ASSERT(!window->mSessionAccessibility.IsAttached());
1765 if (auto gvsAccess{gvs.Access()}) {
1766 gvsAccess->AttachAccessibility(inst, sa);
1771 if (mIsReady) {
1772 // We're in a transfer; update init-data and notify JS code.
1773 mWindow->mAndroidView->mInitData = java::GeckoBundle::Ref::From(aInitData);
1774 OnReady(aQueue);
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);
1789 } else {
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(
1804 inst.Env());
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);
1821 if (!window) {
1822 return nullptr;
1824 return window->OnLoadRequest(aUri, aWindowType, aFlags, aTriggeringUri,
1825 aHasUserGesture, aIsTopLevel);
1828 void GeckoViewSupport::OnShowDynamicToolbar() const {
1829 GeckoSession::Window::LocalRef window(mGeckoViewWindow);
1830 if (!window) {
1831 return;
1834 window->OnShowDynamicToolbar();
1837 void GeckoViewSupport::OnReady(jni::Object::Param aQueue) {
1838 GeckoSession::Window::LocalRef window(mGeckoViewWindow);
1839 if (!window) {
1840 return;
1842 window->OnReady(aQueue);
1843 mIsReady = true;
1846 void GeckoViewSupport::PassExternalResponse(
1847 java::WebResponse::Param aResponse) {
1848 GeckoSession::Window::LocalRef window(mGeckoViewWindow);
1849 if (!window) {
1850 return;
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();
1863 if (!treeOwner) {
1864 return nullptr;
1866 RefPtr<BrowsingContext> bc;
1867 nsresult rv = treeOwner->GetPrimaryContentBrowsingContext(getter_AddRefs(bc));
1868 if (NS_WARN_IF(NS_FAILED(rv)) || !bc) {
1869 return nullptr;
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.");
1891 return;
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.");
1903 return;
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);
1917 print->Then(
1918 mozilla::GetCurrentSerialEventTarget(), __func__,
1919 [result = java::GeckoResult::GlobalRef(aGeckoResult), stream,
1920 pdfErrorMsg](
1921 const CanonicalBrowsingContext::PrintPromise::ResolveOrRejectValue&
1922 aValue) {
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();
1935 if (!cbc) {
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.");
1942 return;
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);
1953 if (!cbc) {
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.");
1960 return;
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();
1985 /* static */
1986 already_AddRefed<nsWindow> nsWindow::From(nsPIDOMWindowOuter* aDOMWindow) {
1987 nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(aDOMWindow);
1988 return From(widget);
1991 /* static */
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
1998 // PuppetWidget.
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();
2004 return nullptr;
2007 nsWindow* nsWindow::TopWindow() {
2008 if (!gTopLevelWindows.IsEmpty()) return gTopLevelWindows[0];
2009 return nullptr;
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));
2020 #endif
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),
2035 mIsVisible(false),
2036 mParent(nullptr),
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) {
2066 if (parent) {
2067 ALOG(
2068 "Ignoring native parent on Android window [%p], "
2069 "since parent was specified (%p %p)",
2070 (void*)this, (void*)aNativeParent, (void*)aParent);
2071 } else {
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) {
2082 rect.width = 0;
2083 rect.height = 0;
2086 mBounds = rect;
2087 SetSizeConstraints(SizeConstraints());
2089 BaseCreate(nullptr, aInitData);
2091 NS_ASSERTION(IsTopLevel() || parent,
2092 "non-top-level window doesn't have a parent!");
2094 if (IsTopLevel()) {
2095 gTopLevelWindows.AppendElement(this);
2097 } else if (parent) {
2098 parent->mChildren.AppendElement(this);
2099 mParent = parent;
2102 #ifdef DEBUG_ANDROID_WIDGET
2103 DumpWindows();
2104 #endif
2106 return NS_OK;
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
2128 // deleted
2129 nsBaseWidget::DestroyCompositor();
2131 nsBaseWidget::Destroy();
2133 if (IsTopLevel()) gTopLevelWindows.RemoveElement(this);
2135 SetParent(nullptr);
2137 nsBaseWidget::OnDestroy();
2139 #ifdef DEBUG_ANDROID_WIDGET
2140 DumpWindows();
2141 #endif
2144 mozilla::widget::EventDispatcher* nsWindow::GetEventDispatcher() const {
2145 if (mAndroidView) {
2146 return mAndroidView->mEventDispatcher;
2148 return nullptr;
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()
2162 : nullptr;
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());
2172 if (!acc) {
2173 return;
2176 acc->OnReady();
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
2183 // children.
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,
2199 bool aIsTopLevel) {
2200 auto geckoViewSupport(mGeckoViewSupport.Access());
2201 if (!geckoViewSupport) {
2202 return MozPromise<bool, bool, false>::CreateAndResolve(false, __func__);
2205 nsAutoCString spec, triggeringSpec;
2206 if (aUri) {
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,
2231 aIsTopLevel);
2232 return geckoResult
2233 ? MozPromise<bool, bool, false>::FromGeckoResult(geckoResult)
2234 : nullptr;
2237 void nsWindow::OnUpdateSessionStore(mozilla::jni::Object::Param aBundle) {
2238 auto geckoViewSupport(mGeckoViewSupport.Access());
2239 if (!geckoViewSupport) {
2240 return;
2243 geckoViewSupport->OnUpdateSessionStore(aBundle);
2246 float nsWindow::GetDPI() {
2247 float dpi = 160.0f;
2249 nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
2250 if (screen) {
2251 screen->GetDpi(&dpi);
2254 return dpi;
2257 double nsWindow::GetDefaultScaleInternal() {
2258 double scale = 1.0f;
2260 nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
2261 if (screen) {
2262 screen->GetContentsScaleFactor(&scale);
2265 return 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..");
2273 return;
2276 if (aState == mIsVisible) return;
2278 mIsVisible = aState;
2280 if (IsTopLevel()) {
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.
2289 if (aState) {
2290 // It just became visible, so bring it to the front.
2291 BringToFront();
2293 } else if (nsWindow::TopWindow() == this) {
2294 // find the next visible window to show
2295 unsigned int i;
2296 for (i = 1; i < gTopLevelWindows.Length(); i++) {
2297 nsWindow* win = gTopLevelWindows[i];
2298 if (!win->mIsVisible) continue;
2300 win->BringToFront();
2301 break;
2304 } else if (FindTopLevel() == nsWindow::TopWindow()) {
2305 RedrawAll();
2308 #ifdef DEBUG_ANDROID_WIDGET
2309 DumpWindows();
2310 #endif
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,
2317 aPoint.y.value);
2319 // Constrain toplevel windows; children we don't care about
2320 if (IsTopLevel()) {
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,
2336 bool aRepaint) {
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) {
2370 return;
2373 mSizeMode = aMode;
2375 switch (aMode) {
2376 case nsSizeMode_Minimized:
2377 java::GeckoAppShell::MoveTaskToBack();
2378 break;
2379 case nsSizeMode_Fullscreen:
2380 MakeFullScreen(true);
2381 break;
2382 default:
2383 break;
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;
2397 while (toplevel) {
2398 if (toplevel->IsTopLevel()) return toplevel;
2400 toplevel = toplevel->mParent;
2403 ALOG(
2404 "nsWindow::FindTopLevel(): couldn't find a toplevel or dialog window in "
2405 "this [%p] widget's hierarchy!",
2406 (void*)this);
2407 return this;
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
2419 // be focused.
2420 nsFocusManager* fm = nsFocusManager::GetFocusManager();
2421 if (fm && fm->GetActiveWindow() && FindTopLevel() == nsWindow::TopWindow()) {
2422 return;
2425 if (!IsTopLevel()) {
2426 FindTopLevel()->BringToFront();
2427 return;
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);
2440 if (oldTop) {
2441 nsIWidgetListener* listener = oldTop->GetWidgetListener();
2442 if (listener) {
2443 listener->WindowDeactivated();
2447 if (mWidgetListener) {
2448 mWidgetListener->WindowActivated();
2451 RedrawAll();
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()) {
2466 break;
2469 return p;
2472 nsresult nsWindow::DispatchEvent(WidgetGUIEvent* aEvent,
2473 nsEventStatus& aStatus) {
2474 aStatus = DispatchEvent(aEvent);
2475 return NS_OK;
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();
2497 if (listener) {
2498 mSizeMode = mIsFullScreen ? nsSizeMode_Fullscreen : nsSizeMode_Normal;
2499 listener->SizeModeChanged(mSizeMode);
2501 return NS_OK;
2504 mozilla::WindowRenderer* nsWindow::GetWindowRenderer() {
2505 if (!mWindowRenderer) {
2506 CreateLayerManager();
2509 return mWindowRenderer;
2512 void nsWindow::CreateLayerManager() {
2513 if (mWindowRenderer) {
2514 return;
2517 nsWindow* topLevelWindow = FindTopLevel();
2518 if (!topLevelWindow || topLevelWindow->mWindowType == WindowType::Invisible) {
2519 // don't create a layer manager for an invisible top-level window
2520 return;
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()) {
2531 DispatchToUiThread(
2532 "LayerViewSupport::NotifyCompositorCreated",
2533 [lvs = mLayerViewSupport,
2534 uiCompositorController = GetUiCompositorControllerChild()] {
2535 if (auto lvsAccess{lvs.Access()}) {
2536 lvsAccess->NotifyCompositorCreated(uiCompositorController);
2541 return;
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();
2565 RedrawAll();
2568 void nsWindow::ShowDynamicToolbar() {
2569 auto acc(mGeckoViewSupport.Access());
2570 if (!acc) {
2571 return;
2574 acc->OnShowDynamicToolbar();
2577 void GeckoViewSupport::OnUpdateSessionStore(
2578 mozilla::jni::Object::Param aBundle) {
2579 GeckoSession::Window::LocalRef window(mGeckoViewWindow);
2580 if (!window) {
2581 return;
2584 window->OnUpdateSessionStore(aBundle);
2587 void nsWindow::OnSizeChanged(const gfx::IntSize& aSize) {
2588 ALOG("nsWindow: %p OnSizeChanged [%d %d]", (void*)this, aSize.width,
2589 aSize.height);
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) {
2606 if (aPoint) {
2607 event.mRefPoint = *aPoint;
2608 } else {
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);
2619 return;
2622 DispatchToUiThread(
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);
2636 return;
2639 DispatchToUiThread(
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:
2651 return (void*)this;
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();
2667 return nullptr;
2670 return nullptr;
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());
2690 if (!acc) {
2691 return;
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.
2716 int64_t tick =
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");
2726 if (mIdleService) {
2727 mIdleService->ResetIdleTimeOut(0);
2730 if (FindTopLevel() != nsWindow::TopWindow()) {
2731 BringToFront();
2735 RefPtr<mozilla::a11y::SessionAccessibility>
2736 nsWindow::GetSessionAccessibility() {
2737 auto acc(mSessionAccessibility.Access());
2738 if (!acc) {
2739 return nullptr;
2742 return acc.AsRefPtr();
2745 TextEventDispatcherListener* nsWindow::GetNativeTextEventDispatcherListener() {
2746 nsWindow* top = FindTopLevel();
2747 MOZ_ASSERT(top);
2749 auto acc(top->mEditableSupport.Access());
2750 if (!acc) {
2751 // Non-GeckoView windows don't support IME operations.
2752 return nullptr;
2755 nsCOMPtr<TextEventDispatcherListener> ptr;
2756 if (NS_FAILED(acc->QueryInterface(NS_GET_IID(TextEventDispatcherListener),
2757 getter_AddRefs(ptr)))) {
2758 return nullptr;
2761 return ptr.get();
2764 void nsWindow::SetInputContext(const InputContext& aContext,
2765 const InputContextAction& aAction) {
2766 nsWindow* top = FindTopLevel();
2767 MOZ_ASSERT(top);
2769 auto acc(top->mEditableSupport.Access());
2770 if (!acc) {
2771 // Non-GeckoView windows don't support IME operations.
2772 return;
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();
2784 MOZ_ASSERT(top);
2786 auto acc(top->mEditableSupport.Access());
2787 if (!acc) {
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");
2805 int eventType;
2806 switch (aPointerState) {
2807 case TOUCH_CONTACT:
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;
2811 break;
2812 case TOUCH_REMOVE:
2813 // This could be turned into a ACTION_UP in Java
2814 eventType = java::sdk::MotionEvent::ACTION_POINTER_UP;
2815 break;
2816 case TOUCH_CANCEL:
2817 eventType = java::sdk::MotionEvent::ACTION_CANCEL;
2818 break;
2819 case TOUCH_HOVER: // not supported for now
2820 default:
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;
2833 DispatchToUiThread(
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);
2841 return NS_OK;
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;
2863 break;
2864 case NativeMouseMessage::ButtonUp:
2865 nativeMessage = java::sdk::MotionEvent::ACTION_POINTER_UP;
2866 break;
2867 case NativeMouseMessage::Move:
2868 nativeMessage = java::sdk::MotionEvent::ACTION_HOVER_MOVE;
2869 break;
2870 default:
2871 MOZ_ASSERT_UNREACHABLE("Non supported mouse event on Android");
2872 return NS_ERROR_INVALID_ARG;
2874 int32_t button = 0;
2875 if (aNativeMessage != NativeMouseMessage::ButtonUp) {
2876 switch (aButton) {
2877 case MouseButton::ePrimary:
2878 button = java::sdk::MotionEvent::BUTTON_PRIMARY;
2879 break;
2880 case MouseButton::eMiddle:
2881 button = java::sdk::MotionEvent::BUTTON_TERTIARY;
2882 break;
2883 case MouseButton::eSecondary:
2884 button = java::sdk::MotionEvent::BUTTON_SECONDARY;
2885 break;
2886 case MouseButton::eX1:
2887 button = java::sdk::MotionEvent::BUTTON_BACK;
2888 break;
2889 case MouseButton::eX2:
2890 button = java::sdk::MotionEvent::BUTTON_FORWARD;
2891 break;
2892 default:
2893 if (aNativeMessage == NativeMouseMessage::ButtonDown) {
2894 MOZ_ASSERT_UNREACHABLE("Non supported mouse button type on Android");
2895 return NS_ERROR_INVALID_ARG;
2897 break;
2901 // TODO (bug 1693237): Handle aModifierFlags.
2902 DispatchToUiThread(
2903 "nsWindow::SynthesizeNativeMouseEvent",
2904 [npzc = java::PanZoomController::NativeProvider::GlobalRef(npzc),
2905 nativeMessage, aPoint, button] {
2906 npzc->SynthesizeNativeMouseEvent(nativeMessage, aPoint.x, aPoint.y,
2907 button);
2909 return NS_OK;
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) {
2920 if (delegate) {
2921 mCompositorWidgetDelegate = delegate->AsPlatformSpecificDelegate();
2922 MOZ_ASSERT(mCompositorWidgetDelegate,
2923 "nsWindow::SetCompositorWidgetDelegate called with a "
2924 "non-PlatformCompositorWidgetDelegate");
2925 } else {
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()) {
2943 return false;
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()
2973 : nullptr;
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,
3000 aZoom.scale);
3004 void nsWindow::RecvScreenPixels(Shmem&& aMem, const ScreenIntSize& aSize,
3005 bool aNeedsYFlip) {
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) {
3015 return;
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()) {
3070 return nullptr;
3073 RefPtr<DataSourceSurface> destDataSurface;
3075 nsIntSize size = nsIWidget::CustomCursorSize(aCursor);
3076 // prevent DoS attacks
3077 if (size.width > 128 || size.height > 128) {
3078 return nullptr;
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)) {
3085 return nullptr;
3088 RefPtr<DataSourceSurface> srcDataSurface = surface->GetDataSurface();
3089 if (NS_WARN_IF(!srcDataSurface)) {
3090 return nullptr;
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)) {
3100 return nullptr;
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.
3116 switch (aCursor) {
3117 case eCursor_standard:
3118 // android.view.PointerIcon.TYPE_ARROW
3119 return 0x3e8;
3120 case eCursor_wait:
3121 // android.view.PointerIcon.TYPE_WAIT
3122 return 0x3ec;
3123 case eCursor_select:
3124 // android.view.PointerIcon.TYPE_TEXT;
3125 return 0x3f0;
3126 case eCursor_hyperlink:
3127 // android.view.PointerIcon.TYPE_HAND
3128 return 0x3ea;
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
3134 return 0x3f7;
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
3140 return 0x3f6;
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
3145 return 0x3f9;
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
3150 return 0x3f8;
3151 case eCursor_crosshair:
3152 // android.view.PointerIcon.TYPE_CROSSHAIR
3153 return 0x3ef;
3154 case eCursor_move:
3155 // android.view.PointerIcon.TYPE_ARROW
3156 return 0x3e8;
3157 case eCursor_help:
3158 // android.view.PointerIcon.TYPE_HELP
3159 return 0x3eb;
3160 case eCursor_copy:
3161 // android.view.PointerIcon.TYPE_COPY
3162 return 0x3f3;
3163 case eCursor_alias:
3164 // android.view.PointerIcon.TYPE_ALIAS
3165 return 0x3f2;
3166 case eCursor_context_menu:
3167 // android.view.PointerIcon.TYPE_CONTEXT_MENU
3168 return 0x3e9;
3169 case eCursor_cell:
3170 // android.view.PointerIcon.TYPE_CELL
3171 return 0x3ee;
3172 case eCursor_grab:
3173 // android.view.PointerIcon.TYPE_GRAB
3174 return 0x3fc;
3175 case eCursor_grabbing:
3176 // android.view.PointerIcon.TYPE_GRABBING
3177 return 0x3fd;
3178 case eCursor_spinning:
3179 // android.view.PointerIcon.TYPE_WAIT
3180 return 0x3ec;
3181 case eCursor_zoom_in:
3182 // android.view.PointerIcon.TYPE_ZOOM_IN
3183 return 0x3fa;
3184 case eCursor_zoom_out:
3185 // android.view.PointerIcon.TYPE_ZOOM_OUT
3186 return 0x3fb;
3187 case eCursor_not_allowed:
3188 // android.view.PointerIcon.TYPE_NO_DROP:
3189 return 0x3f4;
3190 case eCursor_no_drop:
3191 // android.view.PointerIcon.TYPE_NO_DROP:
3192 return 0x3f4;
3193 case eCursor_vertical_text:
3194 // android.view.PointerIcon.TYPE_VERTICAL_TEXT
3195 return 0x3f1;
3196 case eCursor_all_scroll:
3197 // android.view.PointerIcon.TYPE_ALL_SCROLL
3198 return 0x3f5;
3199 case eCursor_none:
3200 // android.view.PointerIcon.TYPE_NULL
3201 return 0;
3202 default:
3203 NS_WARNING_ASSERTION(aCursor, "Invalid cursor type");
3204 // android.view.PointerIcon.TYPE_ARROW
3205 return 0x3e8;
3209 void nsWindow::SetCursor(const Cursor& aCursor) {
3210 if (mozilla::jni::GetAPIVersion() < 24) {
3211 return;
3214 // Only change cursor if it's actually been changed
3215 if (!mUpdateCursor && mCursor == aCursor) {
3216 return;
3219 mUpdateCursor = false;
3220 mCursor = aCursor;
3222 int32_t type = 0;
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();
3233 DispatchToUiThread(
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);