Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/widget/android/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 112 kB image not shown  

SSL nsWindow.cpp   Interaktion und
PortierbarkeitC

 
/* -*- Mode: c++; c-basic-offset: 2; tab-width: 4; indent-tabs-mode: nil; -*-
 * vim: set sw=2 ts=4 expandtab:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include <algorithm>
#include <atomic>
#include <android/log.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include <math.h>
#include <queue>
#include <type_traits>
#include <unistd.h>

#include "AndroidBridge.h"
#include "AndroidBridgeUtilities.h"
#include "AndroidCompositorWidget.h"
#include "AndroidContentController.h"
#include "AndroidDragEvent.h"
#include "AndroidUiThread.h"
#include "AndroidView.h"
#include "AndroidWidgetUtils.h"
#include "gfxContext.h"
#include "GeckoEditableSupport.h"
#include "GeckoViewOutputStream.h"
#include "GeckoViewSupport.h"
#include "GLContext.h"
#include "GLContextProvider.h"
#include "JavaBuiltins.h"
#include "JavaExceptions.h"
#include "KeyEvent.h"
#include "MotionEvent.h"
#include "ScopedGLHelpers.h"
#include "ScreenHelperAndroid.h"
#include "TouchResampler.h"
#include "WidgetUtils.h"
#include "WindowRenderer.h"

#include "mozilla/EventForwards.h"
#include "nsAppShell.h"
#include "nsContentUtils.h"
#include "nsFocusManager.h"
#include "nsGkAtoms.h"
#include "nsGfxCIID.h"
#include "nsIDocShellTreeOwner.h"
#include "nsLayoutUtils.h"
#include "nsNetUtil.h"
#include "nsPrintfCString.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "nsUserIdleService.h"
#include "nsViewManager.h"
#include "nsWidgetsCID.h"
#include "nsWindow.h"

#include "nsIWidgetListener.h"
#include "nsIWindowWatcher.h"
#include "nsIAppWindow.h"
#include "nsIPrintSettings.h"
#include "nsIPrintSettingsService.h"

#include "mozilla/Logging.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_android.h"
#include "mozilla/StaticPrefs_ui.h"
#include "mozilla/TouchEvents.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/WheelHandlingHelper.h"  // for WheelDeltaAdjustmentStrategy
#include "mozilla/a11y/SessionAccessibility.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/BrowserHost.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/gfx/Swizzle.h"
#include "mozilla/gfx/Types.h"
#include "mozilla/ipc/Shmem.h"
#include "mozilla/java/EventDispatcherWrappers.h"
#include "mozilla/java/GeckoAppShellWrappers.h"
#include "mozilla/java/GeckoEditableChildWrappers.h"
#include "mozilla/java/GeckoResultWrappers.h"
#include "mozilla/java/GeckoSessionNatives.h"
#include "mozilla/java/GeckoSystemStateListenerWrappers.h"
#include "mozilla/java/PanZoomControllerNatives.h"
#include "mozilla/java/SessionAccessibilityWrappers.h"
#include "mozilla/java/SurfaceControlManagerWrappers.h"
#include "mozilla/jni/NativesInlines.h"
#include "mozilla/layers/APZEventState.h"
#include "mozilla/layers/APZInputBridge.h"
#include "mozilla/layers/APZThreadUtils.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/CompositorOGL.h"
#include "mozilla/layers/CompositorSession.h"
#include "mozilla/layers/LayersTypes.h"
#include "mozilla/layers/UiCompositorControllerChild.h"
#include "mozilla/layers/IAPZCTreeManager.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/widget/AndroidVsync.h"
#include "mozilla/widget/Screen.h"

#define GVS_LOG(...) MOZ_LOG(sGVSupportLog, LogLevel::Warning, (__VA_ARGS__))

using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::layers;
using namespace mozilla::widget;
using namespace mozilla::ipc;

using mozilla::dom::ContentChild;
using mozilla::dom::ContentParent;
using mozilla::gfx::DataSourceSurface;
using mozilla::gfx::IntSize;
using mozilla::gfx::Matrix;
using mozilla::gfx::SurfaceFormat;
using mozilla::java::GeckoSession;
using mozilla::java::sdk::IllegalStateException;
using GeckoPrintException = GeckoSession::GeckoPrintException;
static mozilla::LazyLogModule sGVSupportLog("GeckoViewSupport");

// All the toplevel windows that have been created; these are in
// stacking order, so the window at gTopLevelWindows[0] is the topmost
// one.
MOZ_RUNINIT static nsTArray<nsWindow*> gTopLevelWindows;

static bool sFailedToCreateGLContext = false;

// Multitouch swipe thresholds in inches
static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4;
static const double SWIPE_MIN_DISTANCE_INCHES = 0.6;

static const double kTouchResampleVsyncAdjustMs = 5.0;

static const int32_t INPUT_RESULT_UNHANDLED =
    java::PanZoomController::INPUT_RESULT_UNHANDLED;
static const int32_t INPUT_RESULT_HANDLED =
    java::PanZoomController::INPUT_RESULT_HANDLED;
static const int32_t INPUT_RESULT_HANDLED_CONTENT =
    java::PanZoomController::INPUT_RESULT_HANDLED_CONTENT;
static const int32_t INPUT_RESULT_IGNORED =
    java::PanZoomController::INPUT_RESULT_IGNORED;

static const nsCString::size_type MAX_TOPLEVEL_DATA_URI_LEN = 2 * 1024 * 1024;

// Unique ID given to each widget, to identify it for the
// CompositorSurfaceManager.
static std::atomic<int32_t> sWidgetId{0};

namespace {
template <class Instance, class Impl>
std::enable_if_t<jni::detail::NativePtrPicker<Impl>::value ==
                     jni::detail::NativePtrType::REFPTR,
                 void>
CallAttachNative(Instance aInstance, Impl* aImpl) {
  Impl::AttachNative(aInstance, RefPtr<Impl>(aImpl).get());
}

template <class Instance, class Impl>
std::enable_if_t<jni::detail::NativePtrPicker<Impl>::value ==
                     jni::detail::NativePtrType::OWNING,
                 void>
CallAttachNative(Instance aInstance, Impl* aImpl) {
  Impl::AttachNative(aInstance, UniquePtr<Impl>(aImpl));
}

template <class Lambda>
bool DispatchToUiThread(const char* aName, Lambda&& aLambda) {
  if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
    uiThread->Dispatch(NS_NewRunnableFunction(aName, std::move(aLambda)));
    return true;
  }
  return false;
}
}  // namespace

namespace mozilla {
namespace widget {

// For double click detection
static int64_t sLastMouseDownTime = 0;
static int32_t sLastMouseButtons = 0;
static int32_t sLastClickCount = 0;
static float sLastMouseDownX = 0;
static float sLastMouseDownY = 0;

using WindowPtr = jni::NativeWeakPtr<GeckoViewSupport>;

/**
 * PanZoomController handles its native calls on the UI thread, so make
 * it separate from GeckoViewSupport.
 */

class NPZCSupport final
    : public java::PanZoomController::NativeProvider::Natives<NPZCSupport> {
  WindowPtr mWindow;
  java::PanZoomController::NativeProvider::WeakRef mNPZC;

  // Stores the returnResult of each pending motion event between
  // HandleMotionEvent and FinishHandlingMotionEvent.
  std::queue<std::pair<uint64_t, java::GeckoResult::GlobalRef>>
      mPendingMotionEventReturnResults;

  RefPtr<AndroidVsync> mAndroidVsync;
  TouchResampler mTouchResampler;
  int mPreviousButtons = 0;
  bool mListeningToVsync = false;

  // Only true if mAndroidVsync is non-null and the resampling pref is set.
  bool mTouchResamplingEnabled = false;

  template <typename Lambda>
  class InputEvent final : public nsAppShell::Event {
    java::PanZoomController::NativeProvider::GlobalRef mNPZC;
    Lambda mLambda;

   public:
    InputEvent(const NPZCSupport* aNPZCSupport, Lambda&& aLambda)
        : mNPZC(aNPZCSupport->mNPZC), mLambda(std::move(aLambda)) {}

    void Run() override {
      MOZ_ASSERT(NS_IsMainThread());

      JNIEnv* const env = jni::GetGeckoThreadEnv();
      const auto npzcSupportWeak = GetNative(
          java::PanZoomController::NativeProvider::LocalRef(env, mNPZC));
      if (!npzcSupportWeak) {
        // We already shut down.
        env->ExceptionClear();
        return;
      }

      auto acc = npzcSupportWeak->Access();
      if (!acc) {
        // We already shut down.
        env->ExceptionClear();
        return;
      }

      auto win = acc->mWindow.Access();
      if (!win) {
        // We already shut down.
        env->ExceptionClear();
        return;
      }

      nsWindow* const window = win->GetNsWindow();
      if (!window) {
        // We already shut down.
        env->ExceptionClear();
        return;
      }

      window->UserActivity();
      return mLambda(window);
    }

    bool IsUIEvent() const override { return true; }
  };

  class MOZ_HEAP_CLASS Observer final : public AndroidVsync::Observer {
   public:
    static Observer* Create(jni::NativeWeakPtr<NPZCSupport>&& aNPZCSupport) {
      return new Observer(std::move(aNPZCSupport));
    }

   private:
    // Private constructor, part of a strategy to make sure
    // we're only able to create these on the heap.
    explicit Observer(jni::NativeWeakPtr<NPZCSupport>&& aNPZCSupport)
        : mNPZCSupport(std::move(aNPZCSupport)) {}

    void OnVsync(const TimeStamp& aTimeStamp) override {
      auto accessor = mNPZCSupport.Access();

      if (!accessor) {
        return;
      }

      accessor->mTouchResampler.NotifyFrame(
          aTimeStamp -
          TimeDuration::FromMilliseconds(kTouchResampleVsyncAdjustMs));
      accessor->ConsumeMotionEventsFromResampler();
    }

    void Dispose() override { delete this; }

    jni::NativeWeakPtr<NPZCSupport> mNPZCSupport;
  };

  Observer* mObserver = nullptr;

  template <typename Lambda>
  void PostInputEvent(Lambda&& aLambda) {
    // Use priority queue for input events.
    nsAppShell::PostEvent(
        MakeUnique<InputEvent<Lambda>>(this, std::move(aLambda)));
  }

 public:
  typedef java::PanZoomController::NativeProvider::Natives<NPZCSupport> Base;

  NPZCSupport(WindowPtr aWindow,
              const java::PanZoomController::NativeProvider::LocalRef& aNPZC)
      : mWindow(aWindow), mNPZC(aNPZC) {
#if defined(DEBUG)
    auto win(mWindow.Access());
    MOZ_ASSERT(!!win);
#endif  // defined(DEBUG)

    mAndroidVsync = AndroidVsync::GetInstance();
  }

  ~NPZCSupport() {
    if (mListeningToVsync) {
      MOZ_RELEASE_ASSERT(mAndroidVsync);
      mAndroidVsync->UnregisterObserver(mObserver, AndroidVsync::INPUT);
      mListeningToVsync = false;
    }
  }

  using Base::AttachNative;
  using Base::DisposeNative;

  void OnWeakNonIntrusiveDetach(already_AddRefed<Runnable> aDisposer) {
    RefPtr<Runnable> disposer = aDisposer;
    // There are several considerations when shutting down NPZC. 1) The
    // Gecko thread may destroy NPZC at any time when nsWindow closes. 2)
    // There may be pending events on the Gecko thread when NPZC is
    // destroyed. 3) mWindow may not be available when the pending event
    // runs. 4) The UI thread may destroy NPZC at any time when GeckoView
    // is destroyed. 5) The UI thread may destroy NPZC at the same time as
    // Gecko thread trying to destroy NPZC. 6) There may be pending calls
    // on the UI thread when NPZC is destroyed. 7) mWindow may have been
    // cleared on the Gecko thread when the pending call happens on the UI
    // thread.
    //
    // 1) happens through OnWeakNonIntrusiveDetach, which first notifies the UI
    // thread through Destroy; Destroy then calls DisposeNative, which
    // finally disposes the native instance back on the Gecko thread. Using
    // Destroy to indirectly call DisposeNative here also solves 5), by
    // making everything go through the UI thread, avoiding contention.
    //
    // 2) and 3) are solved by clearing mWindow, which signals to the
    // pending event that we had shut down. In that case the event bails
    // and does not touch mWindow.
    //
    // 4) happens through DisposeNative directly.
    //
    // 6) is solved by keeping a destroyed flag in the Java NPZC instance,
    // and only make a pending call if the destroyed flag is not set.
    //
    // 7) is solved by taking a lock whenever mWindow is modified on the
    // Gecko thread or accessed on the UI thread. That way, we don't
    // release mWindow until the UI thread is done using it, thus avoiding
    // the race condition.

    if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
      auto npzc = java::PanZoomController::NativeProvider::GlobalRef(mNPZC);
      if (!npzc) {
        return;
      }

      uiThread->Dispatch(
          NS_NewRunnableFunction("NPZCSupport::OnWeakNonIntrusiveDetach",
                                 [npzc, disposer = std::move(disposer)] {
                                   npzc->SetAttached(false);
                                   disposer->Run();
                                 }));
    }
  }

  const java::PanZoomController::NativeProvider::Ref& GetJavaNPZC() const {
    return mNPZC;
  }

 public:
  void SetIsLongpressEnabled(bool aIsLongpressEnabled) {
    RefPtr<IAPZCTreeManager> controller;

    if (auto window = mWindow.Access()) {
      nsWindow* gkWindow = window->GetNsWindow();
      if (gkWindow) {
        controller = gkWindow->mAPZC;
      }
    }

    if (controller) {
      controller->SetLongTapEnabled(aIsLongpressEnabled);
    }
  }

  int32_t HandleScrollEvent(int64_t aTime, int32_t aMetaState, float aX,
                            float aY, float aHScroll, float aVScroll) {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());

    RefPtr<IAPZCTreeManager> controller;

    if (auto window = mWindow.Access()) {
      nsWindow* gkWindow = window->GetNsWindow();
      if (gkWindow) {
        controller = gkWindow->mAPZC;
      }
    }

    if (!controller) {
      return INPUT_RESULT_UNHANDLED;
    }

    ScreenPoint origin = ScreenPoint(aX, aY);

    if (StaticPrefs::ui_scrolling_negate_wheel_scroll()) {
      aHScroll = -aHScroll;
      aVScroll = -aVScroll;
    }

    ScrollWheelInput input(
        nsWindow::GetEventTimeStamp(aTime), nsWindow::GetModifiers(aMetaState),
        ScrollWheelInput::SCROLLMODE_SMOOTH,
        ScrollWheelInput::SCROLLDELTA_PIXEL, origin, aHScroll, aVScroll, false,
        // XXX Do we need to support auto-dir scrolling
        // for Android widgets with a wheel device?
        // Currently, I just leave it unimplemented. If
        // we need to implement it, what's the extra work
        // to do?
        WheelDeltaAdjustmentStrategy::eNone);

    APZEventResult result = controller->InputBridge()->ReceiveInputEvent(input);
    if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
      return INPUT_RESULT_IGNORED;
    }

    PostInputEvent([input = std::move(input), result](nsWindow* window) {
      WidgetWheelEvent wheelEvent = input.ToWidgetEvent(window);
      window->ProcessUntransformedAPZEvent(&wheelEvent, result);
    });

    switch (result.GetStatus()) {
      case nsEventStatus_eIgnore:
        return INPUT_RESULT_UNHANDLED;
      case nsEventStatus_eConsumeDoDefault:
        return result.GetHandledResult()->IsHandledByRoot()
                   ? INPUT_RESULT_HANDLED
                   : INPUT_RESULT_HANDLED_CONTENT;
      default:
        MOZ_ASSERT_UNREACHABLE("Unexpected nsEventStatus");
        return INPUT_RESULT_UNHANDLED;
    }
  }

 private:
  static MouseInput::ButtonType GetButtonType(int button) {
    MouseInput::ButtonType result = MouseInput::NONE;

    switch (button) {
      case java::sdk::MotionEvent::BUTTON_PRIMARY:
        result = MouseInput::PRIMARY_BUTTON;
        break;
      case java::sdk::MotionEvent::BUTTON_SECONDARY:
        result = MouseInput::SECONDARY_BUTTON;
        break;
      case java::sdk::MotionEvent::BUTTON_TERTIARY:
        result = MouseInput::MIDDLE_BUTTON;
        break;
      default:
        break;
    }

    return result;
  }

  static int16_t ConvertButtons(int buttons) {
    int16_t result = 0;

    if (buttons & java::sdk::MotionEvent::BUTTON_PRIMARY) {
      result |= MouseButtonsFlag::ePrimaryFlag;
    }
    if (buttons & java::sdk::MotionEvent::BUTTON_SECONDARY) {
      result |= MouseButtonsFlag::eSecondaryFlag;
    }
    if (buttons & java::sdk::MotionEvent::BUTTON_TERTIARY) {
      result |= MouseButtonsFlag::eMiddleFlag;
    }
    if (buttons & java::sdk::MotionEvent::BUTTON_BACK) {
      result |= MouseButtonsFlag::e4thFlag;
    }
    if (buttons & java::sdk::MotionEvent::BUTTON_FORWARD) {
      result |= MouseButtonsFlag::e5thFlag;
    }

    return result;
  }

  static int32_t ConvertAPZHandledPlace(APZHandledPlace aHandledPlace) {
    switch (aHandledPlace) {
      case APZHandledPlace::Unhandled:
        return INPUT_RESULT_UNHANDLED;
      case APZHandledPlace::HandledByRoot:
        return INPUT_RESULT_HANDLED;
      case APZHandledPlace::HandledByContent:
        return INPUT_RESULT_HANDLED_CONTENT;
      case APZHandledPlace::Invalid:
        MOZ_ASSERT_UNREACHABLE("The handled result should NOT be Invalid");
        return INPUT_RESULT_UNHANDLED;
    }
    MOZ_ASSERT_UNREACHABLE("Unknown handled result");
    return INPUT_RESULT_UNHANDLED;
  }

  static int32_t ConvertSideBits(SideBits aSideBits) {
    int32_t ret = java::PanZoomController::SCROLLABLE_FLAG_NONE;
    if (aSideBits & SideBits::eTop) {
      ret |= java::PanZoomController::SCROLLABLE_FLAG_TOP;
    }
    if (aSideBits & SideBits::eRight) {
      ret |= java::PanZoomController::SCROLLABLE_FLAG_RIGHT;
    }
    if (aSideBits & SideBits::eBottom) {
      ret |= java::PanZoomController::SCROLLABLE_FLAG_BOTTOM;
    }
    if (aSideBits & SideBits::eLeft) {
      ret |= java::PanZoomController::SCROLLABLE_FLAG_LEFT;
    }
    return ret;
  }

  static int32_t ConvertScrollDirections(
      layers::ScrollDirections aScrollDirections) {
    int32_t ret = java::PanZoomController::OVERSCROLL_FLAG_NONE;
    if (aScrollDirections.contains(layers::HorizontalScrollDirection)) {
      ret |= java::PanZoomController::OVERSCROLL_FLAG_HORIZONTAL;
    }
    if (aScrollDirections.contains(layers::VerticalScrollDirection)) {
      ret |= java::PanZoomController::OVERSCROLL_FLAG_VERTICAL;
    }
    return ret;
  }

  static java::PanZoomController::InputResultDetail::LocalRef
  ConvertAPZHandledResult(const APZHandledResult& aHandledResult) {
    return java::PanZoomController::InputResultDetail::New(
        ConvertAPZHandledPlace(aHandledResult.mPlace),
        ConvertSideBits(aHandledResult.mScrollableDirections),
        ConvertScrollDirections(aHandledResult.mOverscrollDirections));
  }

  static bool IsIntoDoubleClickThreshold(float aX, float aY) {
    int32_t deltaX = abs((int32_t)floorf(sLastMouseDownX - aX));
    int32_t deltaY = abs((int32_t)floorf(sLastMouseDownY - aY));
    int32_t threshold = StaticPrefs::widget_double_click_threshold();

    return (deltaX * deltaX + deltaY * deltaY < threshold * threshold);
  }

  static bool IsDoubleClick(int64_t aTime, float aX, float aY, int buttons) {
    if (sLastMouseButtons != buttons) {
      return false;
    }

    int64_t deltaTime = aTime - sLastMouseDownTime;
    if (deltaTime < (int64_t)StaticPrefs::widget_double_click_min() ||
        deltaTime > (int64_t)StaticPrefs::widget_double_click_timeout()) {
      return false;
    }

    return IsIntoDoubleClickThreshold(aX, aY);
  }

 public:
  int32_t HandleMouseEvent(int32_t aAction, int64_t aTime, int32_t aMetaState,
                           float aX, float aY, int buttons) {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());

    RefPtr<IAPZCTreeManager> controller;

    if (auto window = mWindow.Access()) {
      nsWindow* gkWindow = window->GetNsWindow();
      if (gkWindow) {
        controller = gkWindow->mAPZC;
      }
    }

    if (!controller) {
      return INPUT_RESULT_UNHANDLED;
    }

    MouseInput::MouseType mouseType = MouseInput::MOUSE_NONE;
    MouseInput::ButtonType buttonType = MouseInput::NONE;
    switch (aAction) {
      case java::sdk::MotionEvent::ACTION_DOWN:
        mouseType = MouseInput::MOUSE_DOWN;
        buttonType = GetButtonType(buttons ^ mPreviousButtons);
        mPreviousButtons = buttons;

        if (IsDoubleClick(aTime, aX, aY, buttons)) {
          sLastClickCount++;
        } else {
          sLastClickCount = 1;
        }
        sLastMouseDownTime = aTime;
        sLastMouseDownX = aX;
        sLastMouseDownY = aY;
        sLastMouseButtons = buttons;
        break;
      case java::sdk::MotionEvent::ACTION_UP:
        mouseType = MouseInput::MOUSE_UP;
        buttonType = GetButtonType(buttons ^ mPreviousButtons);
        mPreviousButtons = buttons;
        break;
      case java::sdk::MotionEvent::ACTION_MOVE:
        mouseType = MouseInput::MOUSE_MOVE;

        if (!IsIntoDoubleClickThreshold(aX, aY)) {
          sLastClickCount = 0;
        }
        break;
      case java::sdk::MotionEvent::ACTION_HOVER_MOVE:
        mouseType = MouseInput::MOUSE_MOVE;
        break;
      case java::sdk::MotionEvent::ACTION_HOVER_ENTER:
        mouseType = MouseInput::MOUSE_WIDGET_ENTER;
        break;
      case java::sdk::MotionEvent::ACTION_HOVER_EXIT:
        mouseType = MouseInput::MOUSE_WIDGET_EXIT;
        break;
      default:
        break;
    }

    if (mouseType == MouseInput::MOUSE_NONE) {
      return INPUT_RESULT_UNHANDLED;
    }

    ScreenPoint origin = ScreenPoint(aX, aY);

    MouseInput input(
        mouseType, buttonType, MouseEvent_Binding::MOZ_SOURCE_MOUSE,
        ConvertButtons(buttons), origin, nsWindow::GetEventTimeStamp(aTime),
        nsWindow::GetModifiers(aMetaState));

    APZEventResult result = controller->InputBridge()->ReceiveInputEvent(input);
    if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
      return INPUT_RESULT_IGNORED;
    }

    PostInputEvent([input = std::move(input), result,
                    clickCount = sLastClickCount](nsWindow* window) {
      WidgetMouseEvent mouseEvent =
          input.ToWidgetEvent<WidgetMouseEvent>(window);
      mouseEvent.mClickCount = clickCount;
      window->ProcessUntransformedAPZEvent(&mouseEvent, result);
      if (MouseInput::SECONDARY_BUTTON == input.mButtonType) {
        if ((StaticPrefs::ui_context_menus_after_mouseup() &&
             MouseInput::MOUSE_UP == input.mType) ||
            (!StaticPrefs::ui_context_menus_after_mouseup() &&
             MouseInput::MOUSE_DOWN == input.mType)) {
          MouseInput contextMenu = input;

          // Actually we don't dispatch context menu event to APZ since we don't
          // handle it on APZ yet. If handling it, we need to consider how to
          // dispatch it on APZ thread. It may cause a race condition.
          contextMenu.mType = MouseInput::MOUSE_CONTEXTMENU;

          if (contextMenu.IsPointerEventType()) {
            WidgetPointerEvent contextMenuEvent =
                contextMenu.ToWidgetEvent<WidgetPointerEvent>(window);
            window->ProcessUntransformedAPZEvent(&contextMenuEvent, result);
          } else {
            WidgetMouseEvent contextMenuEvent =
                contextMenu.ToWidgetEvent<WidgetMouseEvent>(window);
            window->ProcessUntransformedAPZEvent(&contextMenuEvent, result);
          }
        }
      }
    });

    switch (result.GetStatus()) {
      case nsEventStatus_eIgnore:
        return INPUT_RESULT_UNHANDLED;
      case nsEventStatus_eConsumeDoDefault:
        return result.GetHandledResult()->IsHandledByRoot()
                   ? INPUT_RESULT_HANDLED
                   : INPUT_RESULT_HANDLED_CONTENT;
      default:
        MOZ_ASSERT_UNREACHABLE("Unexpected nsEventStatus");
        return INPUT_RESULT_UNHANDLED;
    }
  }

  // Convert MotionEvent touch radius and orientation into the format required
  // by w3c touchevents.
  // toolMajor and toolMinor span a rectangle that's oriented as per
  // aOrientation, centered around the touch point.
  static std::pair<float, ScreenSize> ConvertOrientationAndRadius(
      float aOrientation, float aToolMajor, float aToolMinor) {
    float angle = aOrientation * 180.0f / M_PI;
    // w3c touchevents spec does not allow orientations == 90
    // this shifts it to -90, which will be shifted to zero below
    if (angle >= 90.0) {
      angle -= 180.0f;
    }

    // w3c touchevent radii are given with an orientation between 0 and
    // 90. The radii are found by removing the orientation and
    // measuring the x and y radii of the resulting ellipse. For
    // Android orientations >= 0 and < 90, use the y radius as the
    // major radius, and x as the minor radius. However, for an
    // orientation < 0, we have to shift the orientation by adding 90,
    // and reverse which radius is major and minor.
    ScreenSize radius;
    if (angle < 0.0f) {
      angle += 90.0f;
      radius =
          ScreenSize(int32_t(aToolMajor / 2.0f), int32_t(aToolMinor / 2.0f));
    } else {
      radius =
          ScreenSize(int32_t(aToolMinor / 2.0f), int32_t(aToolMajor / 2.0f));
    }

    return std::make_pair(angle, radius);
  }

  void HandleMotionEvent(
      const java::PanZoomController::NativeProvider::LocalRef& aInstance,
      jni::Object::Param aEventData, float aScreenX, float aScreenY,
      jni::Object::Param aResult) {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());

    auto returnResult = java::GeckoResult::Ref::From(aResult);
    auto eventData =
        java::PanZoomController::MotionEventData::Ref::From(aEventData);
    nsTArray<int32_t> pointerId(eventData->PointerId()->GetElements());
    size_t pointerCount = pointerId.Length();
    MultiTouchInput::MultiTouchType type;
    size_t startIndex = 0;
    size_t endIndex = pointerCount;

    switch (eventData->Action()) {
      case java::sdk::MotionEvent::ACTION_DOWN:
      case java::sdk::MotionEvent::ACTION_POINTER_DOWN:
        type = MultiTouchInput::MULTITOUCH_START;
        break;
      case java::sdk::MotionEvent::ACTION_MOVE:
        type = MultiTouchInput::MULTITOUCH_MOVE;
        break;
      case java::sdk::MotionEvent::ACTION_UP:
      case java::sdk::MotionEvent::ACTION_POINTER_UP:
        // for pointer-up events we only want the data from
        // the one pointer that went up
        type = MultiTouchInput::MULTITOUCH_END;
        startIndex = eventData->ActionIndex();
        endIndex = startIndex + 1;
        break;
      case java::sdk::MotionEvent::ACTION_OUTSIDE:
      case java::sdk::MotionEvent::ACTION_CANCEL:
        type = MultiTouchInput::MULTITOUCH_CANCEL;
        break;
      default:
        if (returnResult) {
          returnResult->Complete(
              java::sdk::Integer::ValueOf(INPUT_RESULT_UNHANDLED));
        }
        return;
    }

    MultiTouchInput input(type, eventData->Time(),
                          nsWindow::GetEventTimeStamp(eventData->Time()), 0);
    input.modifiers = nsWindow::GetModifiers(eventData->MetaState());
    input.mTouches.SetCapacity(endIndex - startIndex);
    input.mScreenOffset =
        ExternalIntPoint(int32_t(floorf(aScreenX)), int32_t(floorf(aScreenY)));

    size_t historySize = eventData->HistorySize();
    nsTArray<int64_t> historicalTime(
        eventData->HistoricalTime()->GetElements());
    MOZ_RELEASE_ASSERT(historicalTime.Length() == historySize);

    // Each of these is |historySize| sets of |pointerCount| values.
    size_t historicalDataCount = historySize * pointerCount;
    nsTArray<float> historicalX(eventData->HistoricalX()->GetElements());
    nsTArray<float> historicalY(eventData->HistoricalY()->GetElements());
    nsTArray<float> historicalOrientation(
        eventData->HistoricalOrientation()->GetElements());
    nsTArray<float> historicalPressure(
        eventData->HistoricalPressure()->GetElements());
    nsTArray<float> historicalToolMajor(
        eventData->HistoricalToolMajor()->GetElements());
    nsTArray<float> historicalToolMinor(
        eventData->HistoricalToolMinor()->GetElements());

    MOZ_RELEASE_ASSERT(historicalX.Length() == historicalDataCount);
    MOZ_RELEASE_ASSERT(historicalY.Length() == historicalDataCount);
    MOZ_RELEASE_ASSERT(historicalOrientation.Length() == historicalDataCount);
    MOZ_RELEASE_ASSERT(historicalPressure.Length() == historicalDataCount);
    MOZ_RELEASE_ASSERT(historicalToolMajor.Length() == historicalDataCount);
    MOZ_RELEASE_ASSERT(historicalToolMinor.Length() == historicalDataCount);

    // Each of these is |pointerCount| values.
    nsTArray<float> x(eventData->X()->GetElements());
    nsTArray<float> y(eventData->Y()->GetElements());
    nsTArray<float> orientation(eventData->Orientation()->GetElements());
    nsTArray<float> pressure(eventData->Pressure()->GetElements());
    nsTArray<float> toolMajor(eventData->ToolMajor()->GetElements());
    nsTArray<float> toolMinor(eventData->ToolMinor()->GetElements());

    MOZ_ASSERT(x.Length() == pointerCount);
    MOZ_ASSERT(y.Length() == pointerCount);
    MOZ_ASSERT(orientation.Length() == pointerCount);
    MOZ_ASSERT(pressure.Length() == pointerCount);
    MOZ_ASSERT(toolMajor.Length() == pointerCount);
    MOZ_ASSERT(toolMinor.Length() == pointerCount);

    for (size_t i = startIndex; i < endIndex; i++) {
      auto [orien, radius] = ConvertOrientationAndRadius(
          orientation[i], toolMajor[i], toolMinor[i]);

      ScreenIntPoint point(int32_t(floorf(x[i])), int32_t(floorf(y[i])));
      SingleTouchData singleTouchData(pointerId[i], point, radius, orien,
                                      pressure[i]);

      for (size_t historyIndex = 0; historyIndex < historySize;
           historyIndex++) {
        size_t historicalI = historyIndex * pointerCount + i;
        auto [historicalAngle, historicalRadius] = ConvertOrientationAndRadius(
            historicalOrientation[historicalI],
            historicalToolMajor[historicalI], historicalToolMinor[historicalI]);
        ScreenIntPoint historicalPoint(
            int32_t(floorf(historicalX[historicalI])),
            int32_t(floorf(historicalY[historicalI])));
        singleTouchData.mHistoricalData.AppendElement(
            SingleTouchData::HistoricalTouchData{
                nsWindow::GetEventTimeStamp(historicalTime[historyIndex]),
                historicalPoint,
                {},  // mLocalScreenPoint will be computed later by APZ
                historicalRadius,
                historicalAngle,
                historicalPressure[historicalI]});
      }

      input.mTouches.AppendElement(singleTouchData);
    }

    if (mAndroidVsync &&
        eventData->Action() == java::sdk::MotionEvent::ACTION_DOWN) {
      // Query pref value at the beginning of a touch gesture so that we don't
      // leave events stuck in the resampler after a pref flip.
      mTouchResamplingEnabled = StaticPrefs::android_touch_resampling_enabled();
    }

    if (!mTouchResamplingEnabled) {
      FinishHandlingMotionEvent(std::move(input),
                                java::GeckoResult::LocalRef(returnResult));
      return;
    }

    uint64_t eventId = mTouchResampler.ProcessEvent(std::move(input));
    mPendingMotionEventReturnResults.push(
        {eventId, java::GeckoResult::GlobalRef(returnResult)});

    RegisterOrUnregisterForVsync(mTouchResampler.InTouchingState());
    ConsumeMotionEventsFromResampler();
  }

  void RegisterOrUnregisterForVsync(bool aNeedVsync) {
    MOZ_RELEASE_ASSERT(mAndroidVsync);
    if (aNeedVsync && !mListeningToVsync) {
      MOZ_ASSERT(!mObserver);
      auto win = mWindow.Access();
      if (!win) {
        return;
      }
      RefPtr<nsWindow> gkWindow = win->GetNsWindow();
      if (!gkWindow) {
        return;
      }
      MutexAutoLock lock(gkWindow->GetDestroyMutex());
      if (gkWindow->Destroyed()) {
        return;
      }
      jni::NativeWeakPtr<NPZCSupport> weakPtrToThis =
          gkWindow->GetNPZCSupportWeakPtr();
      mObserver = Observer::Create(std::move(weakPtrToThis));
      mAndroidVsync->RegisterObserver(mObserver, AndroidVsync::INPUT);
    } else if (!aNeedVsync && mListeningToVsync) {
      mAndroidVsync->UnregisterObserver(mObserver, AndroidVsync::INPUT);
      mObserver = nullptr;
    }
    mListeningToVsync = aNeedVsync;
  }

  void HandleDragEvent(int32_t aAction, int64_t aTime, float aX, float aY,
                       jni::Object::Param aDropData) {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());

    RefPtr<IAPZCTreeManager> controller;
    if (auto window = mWindow.Access()) {
      if (nsWindow* gkWindow = window->GetNsWindow()) {
        controller = gkWindow->mAPZC;
      }
    }

    if (!controller) {
      return;
    }

    MouseInput::MouseType mouseType = MouseInput::MouseType::MOUSE_NONE;
    switch (aAction) {
      case java::sdk::DragEvent::ACTION_DRAG_STARTED:
        mouseType = MouseInput::MouseType::MOUSE_DRAG_START;
        break;
      case java::sdk::DragEvent::ACTION_DRAG_ENDED:
        mouseType = MouseInput::MouseType::MOUSE_DRAG_END;
        break;
      case java::sdk::DragEvent::ACTION_DRAG_ENTERED:
        mouseType = MouseInput::MouseType::MOUSE_DRAG_ENTER;
        break;
      case java::sdk::DragEvent::ACTION_DRAG_LOCATION:
        mouseType = MouseInput::MouseType::MOUSE_DRAG_OVER;
        break;
      case java::sdk::DragEvent::ACTION_DRAG_EXITED:
        mouseType = MouseInput::MouseType::MOUSE_DRAG_EXIT;
        break;
      case java::sdk::DragEvent::ACTION_DROP:
        mouseType = MouseInput::MouseType::MOUSE_DROP;
        break;
      default:
        break;
    }
    ScreenPoint origin = ScreenPoint(aX, aY);
    MouseInput input(
        mouseType, MouseInput::NONE, MouseEvent_Binding::MOZ_SOURCE_MOUSE, 0,
        origin, nsWindow::GetEventTimeStamp(aTime), nsWindow::GetModifiers(0));

    APZEventResult result = controller->InputBridge()->ReceiveInputEvent(input);
    if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
      return;
    }

    PostInputEvent(
        [input = std::move(input), result, aAction, aX, aY,
         dropData = jni::Object::GlobalRef(aDropData)](nsWindow* window) {
          window->OnDragEvent(aAction, aX, aY, dropData, result, input);
        });
  }

  void ConsumeMotionEventsFromResampler() {
    auto outgoing = mTouchResampler.ConsumeOutgoingEvents();
    while (!outgoing.empty()) {
      auto outgoingEvent = std::move(outgoing.front());
      outgoing.pop();
      java::GeckoResult::GlobalRef returnResult;
      if (outgoingEvent.mEventId) {
        // Look up the GeckoResult for this event.
        // The outgoing events from the resampler are in the same order as the
        // original events, and no event IDs are skipped.
        MOZ_RELEASE_ASSERT(!mPendingMotionEventReturnResults.empty());
        auto pair = mPendingMotionEventReturnResults.front();
        mPendingMotionEventReturnResults.pop();
        MOZ_RELEASE_ASSERT(pair.first == *outgoingEvent.mEventId);
        returnResult = pair.second;
      }
      FinishHandlingMotionEvent(std::move(outgoingEvent.mEvent),
                                java::GeckoResult::LocalRef(returnResult));
    }
  }

  void FinishHandlingMotionEvent(MultiTouchInput&& aInput,
                                 java::GeckoResult::LocalRef&& aReturnResult) {
    RefPtr<IAPZCTreeManager> controller;

    if (auto window = mWindow.Access()) {
      nsWindow* gkWindow = window->GetNsWindow();
      if (gkWindow) {
        controller = gkWindow->mAPZC;
      }
    }

    if (!controller) {
      if (aReturnResult) {
        aReturnResult->Complete(java::PanZoomController::InputResultDetail::New(
            INPUT_RESULT_UNHANDLED,
            java::PanZoomController::SCROLLABLE_FLAG_NONE,
            java::PanZoomController::OVERSCROLL_FLAG_NONE));
      }
      return;
    }

    APZInputBridge::InputBlockCallback callback;
    if (aReturnResult) {
      callback = [aReturnResult = java::GeckoResult::GlobalRef(aReturnResult)](
                     uint64_t aInputBlockId,
                     const APZHandledResult& aHandledResult) {
        aReturnResult->Complete(ConvertAPZHandledResult(aHandledResult));
      };
    }
    APZEventResult result = controller->InputBridge()->ReceiveInputEvent(
        aInput, std::move(callback));

    if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
      if (aReturnResult) {
        if (result.GetHandledResult() != Nothing()) {
          aReturnResult->Complete(
              ConvertAPZHandledResult(result.GetHandledResult().value()));
        } else {
          MOZ_ASSERT_UNREACHABLE(
              "nsEventStatus_eConsumeNoDefault should involve a valid "
              "APZHandledResult");
          aReturnResult->Complete(
              java::PanZoomController::InputResultDetail::New(
                  INPUT_RESULT_IGNORED,
                  java::PanZoomController::SCROLLABLE_FLAG_NONE,
                  java::PanZoomController::OVERSCROLL_FLAG_NONE));
        }
      }
      return;
    }

    // Dispatch APZ input event on Gecko thread.
    PostInputEvent([input = std::move(aInput), result](nsWindow* window) {
      WidgetTouchEvent touchEvent = input.ToWidgetEvent(window);
      window->ProcessUntransformedAPZEvent(&touchEvent, result);
      window->DispatchHitTest(touchEvent);
    });

    if (aReturnResult && result.GetHandledResult() != Nothing()) {
      MOZ_ASSERT(result.GetStatus() == nsEventStatus_eConsumeDoDefault ||
                 result.GetStatus() == nsEventStatus_eIgnore);
      aReturnResult->Complete(
          ConvertAPZHandledResult(result.GetHandledResult().value()));
    }
  }
};

NS_IMPL_ISUPPORTS(AndroidView, nsIGeckoViewEventDispatcher, nsIGeckoViewView)

nsresult AndroidView::GetInitData(JSContext* aCx,
                                  JS::MutableHandle<JS::Value> aOut) {
  if (!mInitData) {
    aOut.setNull();
    return NS_OK;
  }

  return widget::EventDispatcher::UnboxBundle(aCx, mInitData, aOut);
}

/**
 * Compositor has some unique requirements for its native calls, so make it
 * separate from GeckoViewSupport.
 */

class LayerViewSupport final
    : public GeckoSession::Compositor::Natives<LayerViewSupport> {
  WindowPtr mWindow;
  GeckoSession::Compositor::WeakRef mCompositor;
  Atomic<bool, ReleaseAcquire> mCompositorPaused;
  java::sdk::Surface::GlobalRef mSurface;
  java::sdk::SurfaceControl::GlobalRef mSurfaceControl;
  int32_t mX;
  int32_t mY;
  int32_t mWidth;
  int32_t mHeight;
  // Used to communicate with the gecko compositor from the UI thread.
  // Set in NotifyCompositorCreated and cleared in
  // NotifyCompositorSessionLost.
  RefPtr<UiCompositorControllerChild> mUiCompositorControllerChild;
  // Whether we have requested a new Surface from the GeckoSession.
  bool mRequestedNewSurface = false;

  Maybe<uint32_t> mDefaultClearColor;

  struct CaptureRequest {
    explicit CaptureRequest() : mResult(nullptr) {}
    explicit CaptureRequest(java::GeckoResult::GlobalRef aResult,
                            java::sdk::Bitmap::GlobalRef aBitmap,
                            const ScreenRect& aSource,
                            const IntSize& aOutputSize)
        : mResult(aResult),
          mBitmap(aBitmap),
          mSource(aSource),
          mOutputSize(aOutputSize) {}

    // where to send the pixels
    java::GeckoResult::GlobalRef mResult;

    // where to store the pixels
    java::sdk::Bitmap::GlobalRef mBitmap;

    ScreenRect mSource;

    IntSize mOutputSize;
  };
  std::queue<CaptureRequest> mCapturePixelsResults;

  // In order to use Event::HasSameTypeAs in PostTo(), we cannot make
  // LayerViewEvent a template because each template instantiation is
  // a different type. So implement LayerViewEvent as a ProxyEvent.
  class LayerViewEvent final : public nsAppShell::ProxyEvent {
    using Event = nsAppShell::Event;

   public:
    static UniquePtr<Event> MakeEvent(UniquePtr<Event>&& event) {
      return MakeUnique<LayerViewEvent>(std::move(event));
    }

    explicit LayerViewEvent(UniquePtr<Event>&& event)
        : nsAppShell::ProxyEvent(std::move(event)) {}

    void PostTo(LinkedList<Event>& queue) override {
      // Give priority to compositor events, but keep in order with
      // existing compositor events.
      nsAppShell::Event* event = queue.getFirst();
      while (event && event->HasSameTypeAs(this)) {
        event = event->getNext();
      }
      if (event) {
        event->setPrevious(this);
      } else {
        queue.insertBack(this);
      }
    }
  };

 public:
  typedef GeckoSession::Compositor::Natives<LayerViewSupport> Base;

  LayerViewSupport(WindowPtr aWindow,
                   const GeckoSession::Compositor::LocalRef& aInstance)
      : mWindow(aWindow), mCompositor(aInstance), mCompositorPaused(true) {
#if defined(DEBUG)
    auto win(mWindow.Access());
    MOZ_ASSERT(!!win);
#endif  // defined(DEBUG)
  }

  ~LayerViewSupport() {}

  using Base::AttachNative;
  using Base::DisposeNative;

  void OnWeakNonIntrusiveDetach(already_AddRefed<Runnable> aDisposer) {
    RefPtr<Runnable> disposer = aDisposer;
    if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
      GeckoSession::Compositor::GlobalRef compositor(mCompositor);
      if (!compositor) {
        return;
      }

      uiThread->Dispatch(NS_NewRunnableFunction(
          "LayerViewSupport::OnWeakNonIntrusiveDetach",
          [compositor, disposer = std::move(disposer),
           results = &mCapturePixelsResults, window = mWindow]() mutable {
            if (auto accWindow = window.Access()) {
              while (!results->empty()) {
                auto aResult =
                    java::GeckoResult::LocalRef(results->front().mResult);
                if (aResult) {
                  aResult->CompleteExceptionally(
                      java::sdk::IllegalStateException::New(
                          "The compositor has detached from the session")
                          .Cast<jni::Throwable>());
                }
                results->pop();
              }
            }

            compositor->OnCompositorDetached();
            disposer->Run();
          }));
    }
  }

  const GeckoSession::Compositor::Ref& GetJavaCompositor() const {
    return mCompositor;
  }

  bool CompositorPaused() const { return mCompositorPaused; }

  /// Called from the main thread whenever the compositor has been
  /// (re)initialized.
  void NotifyCompositorCreated(
      RefPtr<UiCompositorControllerChild> aUiCompositorControllerChild) {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
    mUiCompositorControllerChild = aUiCompositorControllerChild;

    if (mDefaultClearColor) {
      mUiCompositorControllerChild->SetDefaultClearColor(*mDefaultClearColor);
    }

    if (!mCompositorPaused) {
      // If we are using SurfaceControl but mSurface is null, that means the
      // previous surface was destroyed along with the the previous
      // compositor, and we need to create a new one.
      if (mSurfaceControl && !mSurface) {
        mSurface = java::SurfaceControlManager::GetInstance()->GetChildSurface(
            mSurfaceControl, mWidth, mHeight);
      }

      if (auto window{mWindow.Access()}) {
        nsWindow* gkWindow = window->GetNsWindow();
        if (gkWindow) {
          mUiCompositorControllerChild->OnCompositorSurfaceChanged(
              gkWindow->mWidgetId, mSurface);
        }
      }

      bool resumed = mUiCompositorControllerChild->ResumeAndResize(
          mX, mY, mWidth, mHeight);
      if (!resumed) {
        gfxCriticalNote
            << "Failed to resume compositor from NotifyCompositorCreated";
        RequestNewSurface();
      }
    }
  }

  /// Called from the main thread whenever the compositor has been destroyed.
  void NotifyCompositorSessionLost() {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
    mUiCompositorControllerChild = nullptr;

    if (mSurfaceControl) {
      // If we are using SurfaceControl then we must set the Surface to null
      // here to ensure we create a new one when the new compositor is
      // created.
      mSurface = nullptr;
    }

    if (auto window = mWindow.Access()) {
      while (!mCapturePixelsResults.empty()) {
        auto result =
            java::GeckoResult::LocalRef(mCapturePixelsResults.front().mResult);
        if (result) {
          result->CompleteExceptionally(
              java::sdk::IllegalStateException::New(
                  "Compositor session lost during screen pixels request")
                  .Cast<jni::Throwable>());
        }
        mCapturePixelsResults.pop();
      }
    }
  }

  java::sdk::Surface::Param GetSurface() { return mSurface; }

 private:
  already_AddRefed<DataSourceSurface> FlipScreenPixels(
      Shmem& aMem, const ScreenIntSize& aInSize, const ScreenRect& aInRegion,
      const IntSize& aOutSize) {
    RefPtr<gfx::DataSourceSurface> image =
        gfx::Factory::CreateWrappingDataSourceSurface(
            aMem.get<uint8_t>(),
            StrideForFormatAndWidth(SurfaceFormat::B8G8R8A8, aInSize.width),
            IntSize(aInSize.width, aInSize.height), SurfaceFormat::B8G8R8A8);
    RefPtr<gfx::DrawTarget> drawTarget =
        gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
            aOutSize, SurfaceFormat::B8G8R8A8);
    if (!drawTarget) {
      return nullptr;
    }

    drawTarget->SetTransform(Matrix::Scaling(1.0, -1.0) *
                             Matrix::Translation(0, aOutSize.height));

    gfx::Rect srcRect(aInRegion.x,
                      (aInSize.height - aInRegion.height) - aInRegion.y,
                      aInRegion.width, aInRegion.height);
    gfx::Rect destRect(0, 0, aOutSize.width, aOutSize.height);
    drawTarget->DrawSurface(image, destRect, srcRect);

    RefPtr<gfx::SourceSurface> snapshot = drawTarget->Snapshot();
    RefPtr<gfx::DataSourceSurface> data = snapshot->GetDataSurface();
    return data.forget();
  }

  /**
   * Compositor methods
   */

 public:
  void AttachNPZC(jni::Object::Param aNPZC) {
    MOZ_ASSERT(NS_IsMainThread());
    MOZ_ASSERT(aNPZC);

    auto locked(mWindow.Access());
    if (!locked) {
      return;  // Already shut down.
    }

    nsWindow* gkWindow = locked->GetNsWindow();

    // We can have this situation if we get two GeckoViewSupport::Transfer()
    // called before the first AttachNPZC() gets here. Just detach the current
    // instance since that's what happens in GeckoViewSupport::Transfer() as
    // well.
    gkWindow->mNPZCSupport.Detach();

    auto npzc = java::PanZoomController::NativeProvider::LocalRef(
        jni::GetGeckoThreadEnv(),
        java::PanZoomController::NativeProvider::Ref::From(aNPZC));
    gkWindow->mNPZCSupport =
        jni::NativeWeakPtrHolder<NPZCSupport>::Attach(npzc, mWindow, npzc);

    DispatchToUiThread(
        "LayerViewSupport::AttachNPZC",
        [npzc = java::PanZoomController::NativeProvider::GlobalRef(npzc)] {
          npzc->SetAttached(true);
        });
  }

  void OnBoundsChanged(int32_t aLeft, int32_t aTop, int32_t aWidth,
                       int32_t aHeight) {
    MOZ_ASSERT(NS_IsMainThread());
    auto acc = mWindow.Access();
    if (!acc) {
      return;  // Already shut down.
    }

    nsWindow* gkWindow = acc->GetNsWindow();
    if (!gkWindow) {
      return;
    }

    gkWindow->Resize(aLeft, aTop, aWidth, aHeight, /* repaint */ false);
  }

  void NotifyMemoryPressure() {
    MOZ_ASSERT(NS_IsMainThread());
    auto acc = mWindow.Access();
    if (!acc) {
      return;  // Already shut down.
    }

    nsWindow* gkWindow = acc->GetNsWindow();
    if (!gkWindow || !gkWindow->mCompositorBridgeChild) {
      return;
    }

    gkWindow->mCompositorBridgeChild->SendNotifyMemoryPressure();
  }

  void SetDynamicToolbarMaxHeight(int32_t aHeight) {
    MOZ_ASSERT(NS_IsMainThread());
    auto acc = mWindow.Access();
    if (!acc) {
      return;  // Already shut down.
    }

    nsWindow* gkWindow = acc->GetNsWindow();
    if (!gkWindow) {
      return;
    }

    gkWindow->UpdateDynamicToolbarMaxHeight(ScreenIntCoord(aHeight));
  }

  void OnKeyboardHeightChanged(int32_t aHeight) {
    MOZ_ASSERT(NS_IsMainThread());
    auto win(mWindow.Access());
    if (!win) {
      return;  // Already shut down.
    }

    nsWindow* gkWindow = win->GetNsWindow();
    if (!gkWindow) {
      return;
    }

    gkWindow->KeyboardHeightChanged(ScreenIntCoord(aHeight));
  }

  void SyncPauseCompositor() {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());

    // Set this true prior to attempting to pause the compositor, so that if
    // pausing fails the subsequent recovery knows to initialize the compositor
    // in a paused state.
    mCompositorPaused = true;

    if (mUiCompositorControllerChild) {
      mUiCompositorControllerChild->Pause();

      mSurface = nullptr;
      mSurfaceControl = nullptr;
      if (auto window = mWindow.Access()) {
        nsWindow* gkWindow = window->GetNsWindow();
        if (gkWindow) {
          mUiCompositorControllerChild->OnCompositorSurfaceChanged(
              gkWindow->mWidgetId, nullptr);
        }
      }
    }

    if (auto lock{mWindow.Access()}) {
      while (!mCapturePixelsResults.empty()) {
        auto result =
            java::GeckoResult::LocalRef(mCapturePixelsResults.front().mResult);
        if (result) {
          result->CompleteExceptionally(
              java::sdk::IllegalStateException::New(
                  "The compositor has detached from the session")
                  .Cast<jni::Throwable>());
        }
        mCapturePixelsResults.pop();
      }
    }
  }

  void SyncResumeCompositor() {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());

    // Set this false prior to attempting to resume the compositor, so that if
    // resumption fails the subsequent recovery knows to initialize the
    // compositor in a resumed state.
    mCompositorPaused = false;

    if (mUiCompositorControllerChild) {
      bool resumed = mUiCompositorControllerChild->Resume();
      if (!resumed) {
        gfxCriticalNote
            << "Failed to resume compositor from SyncResumeCompositor";
        RequestNewSurface();
      }
    }
  }

  void SyncResumeResizeCompositor(
      const GeckoSession::Compositor::LocalRef& aObj, int32_t aX, int32_t aY,
      int32_t aWidth, int32_t aHeight, jni::Object::Param aSurface,
      jni::Object::Param aSurfaceControl) {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());

    // Set this false prior to attempting to resume the compositor, so that if
    // resumption fails the subsequent recovery knows to initialize the
    // compositor in a resumed state.
    mCompositorPaused = false;

    mX = aX;
    mY = aY;
    mWidth = aWidth;
    mHeight = aHeight;
    if (StaticPrefs::widget_android_use_surfacecontrol_AtStartup()) {
      mSurfaceControl =
          java::sdk::SurfaceControl::GlobalRef::From(aSurfaceControl);
    }
    if (mSurfaceControl) {
      // When using SurfaceControl, we create a child Surface to render in to
      // rather than rendering directly in to the Surface provided by the
      // application. This allows us to work around a bug on some versions of
      // Android when recovering from a GPU process crash.
      mSurface = java::SurfaceControlManager::GetInstance()->GetChildSurface(
          mSurfaceControl, mWidth, mHeight);
    } else {
      mSurface = java::sdk::Surface::GlobalRef::From(aSurface);
    }

    if (mUiCompositorControllerChild) {
      if (auto window = mWindow.Access()) {
        nsWindow* gkWindow = window->GetNsWindow();
        if (gkWindow) {
          // Send new Surface to GPU process, if one exists.
          mUiCompositorControllerChild->OnCompositorSurfaceChanged(
              gkWindow->mWidgetId, mSurface);
        }
      }

      bool resumed = mUiCompositorControllerChild->ResumeAndResize(
          aX, aY, aWidth, aHeight);
      if (!resumed) {
        gfxCriticalNote
            << "Failed to resume compositor from SyncResumeResizeCompositor";
        // Only request a new Surface if this SyncResumeAndResize call is not
        // response to a previous request, otherwise we will get stuck in an
        // infinite loop.
        if (!mRequestedNewSurface) {
          RequestNewSurface();
        }
        return;
      }
    }

    mRequestedNewSurface = false;

    class OnResumedEvent : public nsAppShell::Event {
      GeckoSession::Compositor::GlobalRef mCompositor;

     public:
      explicit OnResumedEvent(GeckoSession::Compositor::GlobalRef&& aCompositor)
          : mCompositor(std::move(aCompositor)) {}

      void Run() override {
        MOZ_ASSERT(NS_IsMainThread());

        JNIEnv* const env = jni::GetGeckoThreadEnv();
        const auto lvsHolder =
            GetNative(GeckoSession::Compositor::LocalRef(env, mCompositor));

        if (!lvsHolder) {
          env->ExceptionClear();
          return;  // Already shut down.
        }

        auto lvs(lvsHolder->Access());
        if (!lvs) {
          env->ExceptionClear();
          return;  // Already shut down.
        }

        auto win = lvs->mWindow.Access();
        if (!win) {
          env->ExceptionClear();
          return;  // Already shut down.
        }

        // When we get here, the compositor has already been told to
        // resume. This means it's now safe for layer updates to occur.
        // Since we might have prevented one or more draw events from
        // occurring while the compositor was paused, we need to
        // schedule a draw event now.
        if (!lvs->mCompositorPaused) {
          nsWindow* const gkWindow = win->GetNsWindow();
          if (gkWindow) {
            gkWindow->RedrawAll();
          }
        }
      }
    };

    // Use priority queue for timing-sensitive event.
    nsAppShell::PostEvent(
        MakeUnique<LayerViewEvent>(MakeUnique<OnResumedEvent>(aObj)));
  }

  void RequestNewSurface() {
    if (const auto& compositor = GetJavaCompositor()) {
      mRequestedNewSurface = true;
      if (mSurfaceControl) {
        java::SurfaceControlManager::GetInstance()->RemoveSurface(
            mSurfaceControl);
      }
      compositor->RequestNewSurface();
    }
  }

  mozilla::jni::Object::LocalRef GetMagnifiableSurface() {
    return mozilla::jni::Object::LocalRef::From(GetSurface());
  }

  void SyncInvalidateAndScheduleComposite() {
    if (!mUiCompositorControllerChild) {
      return;
    }

    if (AndroidBridge::IsJavaUiThread()) {
      mUiCompositorControllerChild->InvalidateAndRender();
      return;
    }

    if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
      uiThread->Dispatch(NewRunnableMethod<>(
                             "LayerViewSupport::InvalidateAndRender",
                             mUiCompositorControllerChild,
                             &UiCompositorControllerChild::InvalidateAndRender),
                         nsIThread::DISPATCH_NORMAL);
    }
  }

  void SetMaxToolbarHeight(int32_t aHeight) {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());

    if (mUiCompositorControllerChild) {
      mUiCompositorControllerChild->SetMaxToolbarHeight(aHeight);
    }
  }

  void SetFixedBottomOffset(int32_t aOffset) {
    if (auto acc{mWindow.Access()}) {
      nsWindow* gkWindow = acc->GetNsWindow();
      if (gkWindow) {
        gkWindow->UpdateDynamicToolbarOffset(ScreenIntCoord(aOffset));
      }
    }

    if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
      uiThread->Dispatch(NS_NewRunnableFunction(
          "LayerViewSupport::SetFixedBottomOffset", [this, offset = aOffset] {
            if (mUiCompositorControllerChild) {
              mUiCompositorControllerChild->SetFixedBottomOffset(offset);
            }
          }));
    }
  }

  void SendToolbarAnimatorMessage(int32_t aMessage) {
    if (!mUiCompositorControllerChild) {
      return;
    }

    if (AndroidBridge::IsJavaUiThread()) {
      mUiCompositorControllerChild->ToolbarAnimatorMessageFromUI(aMessage);
      return;
    }

    if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
      uiThread->Dispatch(
          NewRunnableMethod<int32_t>(
              "LayerViewSupport::ToolbarAnimatorMessageFromUI",
              mUiCompositorControllerChild,
              &UiCompositorControllerChild::ToolbarAnimatorMessageFromUI,
              aMessage),
          nsIThread::DISPATCH_NORMAL);
    }
  }

  void RecvToolbarAnimatorMessage(int32_t aMessage) {
    auto compositor = GeckoSession::Compositor::LocalRef(mCompositor);
    if (compositor) {
      compositor->RecvToolbarAnimatorMessage(aMessage);
    }
  }

  void SetDefaultClearColor(int32_t aColor) {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
    mDefaultClearColor = Some((uint32_t)aColor);
    if (mUiCompositorControllerChild) {
      mUiCompositorControllerChild->SetDefaultClearColor((uint32_t)aColor);
    }
  }

  void RequestScreenPixels(jni::Object::Param aResult,
                           jni::Object::Param aTarget, int32_t aXOffset,
                           int32_t aYOffset, int32_t aSrcWidth,
                           int32_t aSrcHeight, int32_t aOutWidth,
                           int32_t aOutHeight) {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
    auto result = java::GeckoResult::LocalRef(aResult);

    if (!mUiCompositorControllerChild) {
      if (result) {
        if (auto window = mWindow.Access()) {
          result->CompleteExceptionally(
              java::sdk::IllegalStateException::New(
                  "Compositor session lost prior to screen pixels request")
                  .Cast<jni::Throwable>());
        }
      }
      return;
    }

    int size = 0;
    if (auto window = mWindow.Access()) {
      mCapturePixelsResults.push(CaptureRequest(
          java::GeckoResult::GlobalRef(result),
          java::sdk::Bitmap::GlobalRef(java::sdk::Bitmap::LocalRef(aTarget)),
          ScreenRect(aXOffset, aYOffset, aSrcWidth, aSrcHeight),
          IntSize(aOutWidth, aOutHeight)));
      size = mCapturePixelsResults.size();
    }

    if (size == 1) {
      mUiCompositorControllerChild->RequestScreenPixels();
    }
  }

  void RecvScreenPixels(Shmem&& aMem, const ScreenIntSize& aSize,
                        bool aNeedsYFlip) {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
    CaptureRequest request;
    java::GeckoResult::LocalRef result = nullptr;
    java::sdk::Bitmap::LocalRef bitmap = nullptr;
    if (auto window = mWindow.Access()) {
      // The result might have been already rejected if the compositor was
      // detached from the session
      if (!mCapturePixelsResults.empty()) {
        request = mCapturePixelsResults.front();
        result = java::GeckoResult::LocalRef(request.mResult);
        bitmap = java::sdk::Bitmap::LocalRef(request.mBitmap);
        mCapturePixelsResults.pop();
      }
    }

    if (result) {
      if (bitmap) {
        RefPtr<DataSourceSurface> surf;
        if (aNeedsYFlip) {
          surf = FlipScreenPixels(aMem, aSize, request.mSource,
                                  request.mOutputSize);
        } else {
          surf = gfx::Factory::CreateWrappingDataSourceSurface(
              aMem.get<uint8_t>(),
              StrideForFormatAndWidth(SurfaceFormat::B8G8R8A8, aSize.width),
              IntSize(aSize.width, aSize.height), SurfaceFormat::B8G8R8A8);
        }
        if (surf) {
          DataSourceSurface::ScopedMap smap(surf, DataSourceSurface::READ);
          auto pixels = mozilla::jni::ByteBuffer::New(
              reinterpret_cast<int8_t*>(smap.GetData()),
              smap.GetStride() * request.mOutputSize.height);
          bitmap->CopyPixelsFromBuffer(pixels);
          result->Complete(bitmap);
        } else {
          result->CompleteExceptionally(
              java::sdk::IllegalStateException::New(
                  "Failed to create flipped snapshot surface (probably out "
                  "of memory)")
                  .Cast<jni::Throwable>());
        }
      } else {
        result->CompleteExceptionally(java::sdk::IllegalArgumentException::New(
                                          "No target bitmap argument provided")
                                          .Cast<jni::Throwable>());
      }
    }

    // Pixels have been copied, so Dealloc Shmem
    if (mUiCompositorControllerChild) {
      mUiCompositorControllerChild->DeallocPixelBuffer(aMem);

      if (auto window = mWindow.Access()) {
        if (!mCapturePixelsResults.empty()) {
          mUiCompositorControllerChild->RequestScreenPixels();
        }
      }
    }
  }

  void EnableLayerUpdateNotifications(bool aEnable) {
    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
    if (mUiCompositorControllerChild) {
      mUiCompositorControllerChild->EnableLayerUpdateNotifications(aEnable);
    }
  }

  void OnSafeAreaInsetsChanged(int32_t aTop, int32_t aRight, int32_t aBottom,
                               int32_t aLeft) {
    MOZ_ASSERT(NS_IsMainThread());
    auto win(mWindow.Access());
    if (!win) {
      return;  // Already shut down.
    }

    nsWindow* gkWindow = win->GetNsWindow();
    if (!gkWindow) {
      return;
    }

    LayoutDeviceIntMargin safeAreaInsets(aTop, aRight, aBottom, aLeft);
    gkWindow->UpdateSafeAreaInsets(safeAreaInsets);
  }
};

GeckoViewSupport::~GeckoViewSupport() {
  if (mWindow) {
    mWindow->DetachNatives();
  }
}

/* static */
void GeckoViewSupport::Open(
    const jni::Class::LocalRef& aCls, GeckoSession::Window::Param aWindow,
    jni::Object::Param aQueue, jni::Object::Param aCompositor,
    jni::Object::Param aDispatcher, jni::Object::Param aSessionAccessibility,
    jni::Object::Param aInitData, jni::String::Param aId,
    jni::String::Param aChromeURI, bool aPrivateMode) {
  MOZ_ASSERT(NS_IsMainThread());

  AUTO_PROFILER_LABEL("mozilla::widget::GeckoViewSupport::Open", OTHER);

  // We'll need gfxPlatform to be initialized to create a compositor later.
  // Might as well do that now so that the GPU process launch can get a head
  // start.
  gfxPlatform::GetPlatform();

  nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID);
  MOZ_RELEASE_ASSERT(ww);

  nsAutoCString url;
  if (aChromeURI) {
    url = aChromeURI->ToCString();
  } else {
    nsresult rv = Preferences::GetCString("toolkit.defaultChromeURI", url);
    if (NS_FAILED(rv)) {
      url = "chrome://geckoview/content/geckoview.xhtml"_ns;
    }
  }

  // Prepare an nsIGeckoViewView to pass as argument to the window.
  RefPtr<AndroidView> androidView = new AndroidView();
  androidView->mEventDispatcher->Attach(
      java::EventDispatcher::Ref::From(aDispatcher), nullptr);
  androidView->mInitData = java::GeckoBundle::Ref::From(aInitData);

  nsAutoCString chromeFlags("chrome,dialog=0,remote,resizable,scrollbars");
  if (aPrivateMode) {
    chromeFlags += ",private";
  }
  nsCOMPtr<mozIDOMWindowProxy> domWindow;
  ww->OpenWindow(nullptr, url, nsDependentCString(aId->ToCString().get()),
                 chromeFlags, androidView, getter_AddRefs(domWindow));
  MOZ_RELEASE_ASSERT(domWindow);

  nsCOMPtr<nsPIDOMWindowOuter> pdomWindow = nsPIDOMWindowOuter::From(domWindow);
  const RefPtr<nsWindow> window = nsWindow::From(pdomWindow);
  MOZ_ASSERT(window);

  // Attach a new GeckoView support object to the new window.
  GeckoSession::Window::LocalRef sessionWindow(aCls.Env(), aWindow);
  auto weakGeckoViewSupport =
      jni::NativeWeakPtrHolder<GeckoViewSupport>::Attach(
          sessionWindow, window, sessionWindow, pdomWindow);

  window->mGeckoViewSupport = weakGeckoViewSupport;
  window->mAndroidView = androidView;

  // Attach other session support objects.
  {  // Scope for gvsAccess
    auto gvsAccess = weakGeckoViewSupport.Access();
    MOZ_ASSERT(gvsAccess);

    gvsAccess->Transfer(sessionWindow, aQueue, aCompositor, aDispatcher,
                        aSessionAccessibility, aInitData);
  }

  if (window->mWidgetListener) {
    nsCOMPtr<nsIAppWindow> appWindow(window->mWidgetListener->GetAppWindow());
    if (appWindow) {
      // Our window is not intrinsically sized, so tell AppWindow to
      // not set a size for us.
      appWindow->SetIntrinsicallySized(false);
    }
  }
}

void GeckoViewSupport::Close() {
  if (mWindow) {
    if (mWindow->mAndroidView) {
      mWindow->mAndroidView->mEventDispatcher->Detach();
    }
    mWindow = nullptr;
  }

  if (!mDOMWindow) {
    return;
  }

  mDOMWindow->ForceClose();
  mDOMWindow = nullptr;
  mGeckoViewWindow = nullptr;
}

void GeckoViewSupport::Transfer(const GeckoSession::Window::LocalRef& inst,
                                jni::Object::Param aQueue,
                                jni::Object::Param aCompositor,
                                jni::Object::Param aDispatcher,
                                jni::Object::Param aSessionAccessibility,
                                jni::Object::Param aInitData) {
  mWindow->mNPZCSupport.Detach();

  auto compositor = GeckoSession::Compositor::LocalRef(
      inst.Env(), GeckoSession::Compositor::Ref::From(aCompositor));

  bool attachLvs;
  {  // Scope for lvsAccess
    auto lvsAccess{mWindow->mLayerViewSupport.Access()};
    // If we do not yet have mLayerViewSupport, or if the compositor has
    // changed, then we must attach a new one.
    attachLvs = !lvsAccess || lvsAccess->GetJavaCompositor() != compositor;
  }

  if (attachLvs) {
    mWindow->mLayerViewSupport =
        jni::NativeWeakPtrHolder<LayerViewSupport>::Attach(
            compositor, mWindow->mGeckoViewSupport, compositor);

    if (RefPtr<UiCompositorControllerChild> uiCompositorController =
            mWindow->GetUiCompositorControllerChild()) {
      DispatchToUiThread(
          "LayerViewSupport::NotifyCompositorCreated",
          [lvs = mWindow->mLayerViewSupport, uiCompositorController] {
            if (auto lvsAccess{lvs.Access()}) {
--> --------------------

--> maximum size reached

--> --------------------

98%


¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.23Bemerkung:  (vorverarbeitet)  ¤

*Bot Zugriff






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.