chromium/chrome/browser/ui/views/user_education/browser_help_bubble_event_relay_mac.mm

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/views/user_education/browser_help_bubble_event_relay.h"

#include <memory>

#include "components/remote_cocoa/app_shim/mouse_capture.h"
#include "components/remote_cocoa/app_shim/mouse_capture_delegate.h"
#include "ui/events/event.h"
#include "ui/events/event_target.h"
#include "ui/events/event_utils.h"
#include "ui/gfx/mac/coordinate_conversion.h"
#include "ui/gfx/native_widget_types.h"

class WindowHelpBubbleEventRelayMac::Delegate
    : public remote_cocoa::CocoaMouseCaptureDelegate {
 public:
  Delegate(views::Widget* primary_widget, WindowHelpBubbleEventRelayMac& owner)
      : observed_widget_(primary_widget), owner_(owner) {}
  ~Delegate() override = default;

  bool PostCapturedEvent(NSEvent* event) override {
    std::unique_ptr<ui::Event> ui_event =
        ui::EventFromNative(base::apple::OwnedNSEvent(event));
    if (!ui_event || !ui_event->IsLocatedEvent()) {
      return false;
    }

    NSPoint screen_point = event.locationInWindow;
    // If there's no window, the event is in screen coordinates.
    if (event.window) {
      NSRect frame = [event.window contentRectForFrameRect:event.window.frame];
      screen_point.x += frame.origin.x;
      screen_point.y += frame.origin.y;
    }
    const gfx::Point screen_coords = gfx::ScreenPointFromNSPoint(screen_point);

    ui::LocatedEvent* const located_event = ui_event->AsLocatedEvent();
    return owner_->OnEvent(*located_event, screen_coords);
  }

  void OnMouseCaptureLost() override { owner_->OnConnectionLost(); }

  NSWindow* GetWindow() const override {
    // This isn't used for this use case and might be null anyway due to in/out
    // of process stuff. Safer to just return null.
    return nullptr;
  }

 private:
  const raw_ptr<views::Widget> observed_widget_;
  const raw_ref<WindowHelpBubbleEventRelayMac> owner_;
  remote_cocoa::CocoaMouseCapture capture_{this};
};

WindowHelpBubbleEventRelayMac::WindowHelpBubbleEventRelayMac(
    views::Widget* primary_widget)
    : WindowHelpBubbleEventRelay(primary_widget),
      delegate_(std::make_unique<Delegate>(primary_widget, *this)) {}

WindowHelpBubbleEventRelayMac::~WindowHelpBubbleEventRelayMac() {
  Release();
}

void WindowHelpBubbleEventRelayMac::Release() {
  delegate_.reset();
  WindowHelpBubbleEventRelay::Release();
}