| // Copyright 2021 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/image_editor/event_capture_mac.h" | 
 |  | 
 | #import <Cocoa/Cocoa.h> | 
 |  | 
 | #include <memory> | 
 |  | 
 | #include "base/apple/owned_objc.h" | 
 | #include "base/check.h" | 
 | #include "base/functional/callback.h" | 
 | #include "base/memory/ptr_util.h" | 
 | #include "base/memory/raw_ptr.h" | 
 | #include "base/memory/weak_ptr.h" | 
 | #import "components/remote_cocoa/app_shim/mouse_capture.h" | 
 | #import "components/remote_cocoa/app_shim/mouse_capture_delegate.h" | 
 | #include "ui/events/event.h" | 
 | #include "ui/events/event_utils.h" | 
 |  | 
 | namespace image_editor { | 
 |  | 
 | class EventCaptureMac::MouseCaptureDelegateImpl | 
 |     : public remote_cocoa::CocoaMouseCaptureDelegate { | 
 |  public: | 
 |   MouseCaptureDelegateImpl(ui::EventHandler* event_handler, | 
 |                            base::OnceClosure capture_lost_callback, | 
 |                            gfx::NativeView web_contents_view, | 
 |                            gfx::NativeWindow target_native_window) | 
 |       : event_handler_(event_handler), | 
 |         capture_lost_callback_(std::move(capture_lost_callback)), | 
 |         web_contents_view_(web_contents_view.GetNativeNSView()), | 
 |         window_(target_native_window.GetNativeNSWindow()), | 
 |         mouse_capture_( | 
 |             std::make_unique<remote_cocoa::CocoaMouseCapture>(this)) {} | 
 |  | 
 |   void SetKeyboardMonitor(id local_keyboard_monitor) { | 
 |     local_keyboard_monitor_ = local_keyboard_monitor; | 
 |   } | 
 |  | 
 |   void Reset() { | 
 |     // We do not want our callback to run if mouse capture loss was caused by | 
 |     // reset of event capture. | 
 |     std::move(capture_lost_callback_).Reset(); | 
 |  | 
 |     // Remove the key down monitor. | 
 |     [NSEvent removeMonitor:local_keyboard_monitor_]; | 
 |   } | 
 |  | 
 |  private: | 
 |   // remote_cocoa::CocoaMouseCaptureDelegate: | 
 |   bool PostCapturedEvent(NSEvent* event) override { | 
 |     std::unique_ptr<ui::Event> ui_event = | 
 |         ui::EventFromNative(base::apple::OwnedNSEvent(event)); | 
 |     if (!ui_event) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     // The window from where the event is sourced. If it is outside of the | 
 |     // browser, this window will not be equal to GetWindow(). | 
 |     NSView* view = [event.window.contentView hitTest:event.locationInWindow]; | 
 |  | 
 |     ui::EventType type = ui_event->type(); | 
 |     if (type == ui::EventType::kMouseDragged || | 
 |         type == ui::EventType::kMouseReleased) { | 
 |       event_handler_->OnMouseEvent(ui_event->AsMouseEvent()); | 
 |     } else if ((type == ui::EventType::kMousePressed || | 
 |                 type == ui::EventType::kMouseMoved) && | 
 |                web_contents_view_ == view) { | 
 |       // We do not need to record mouse clicks outside of the web contents. | 
 |       event_handler_->OnMouseEvent(ui_event->AsMouseEvent()); | 
 |     } else if (type == ui::EventType::kMouseMoved && | 
 |                web_contents_view_ != view) { | 
 |       // Manually set arrow cursor when region search UI is open and cursor is | 
 |       // moved from web contents. | 
 |       [NSCursor.arrowCursor set]; | 
 |     } else if (type == ui::EventType::kScroll) { | 
 |       event_handler_->OnScrollEvent(ui_event->AsScrollEvent()); | 
 |     } | 
 |  | 
 |     // If we set the ui event as handled, then we want to swallow the event. | 
 |     return ui_event->handled(); | 
 |   } | 
 |  | 
 |   void OnMouseCaptureLost() override { | 
 |     if (!capture_lost_callback_.is_null()) { | 
 |       std::move(capture_lost_callback_).Run(); | 
 |     } | 
 |   } | 
 |  | 
 |   NSWindow* GetWindow() const override { return window_; } | 
 |  | 
 |   raw_ptr<ui::EventHandler> event_handler_; | 
 |   base::OnceClosure capture_lost_callback_; | 
 |   NSView* __weak web_contents_view_ = nil; | 
 |   NSWindow* __weak window_ = nil; | 
 |   std::unique_ptr<remote_cocoa::CocoaMouseCapture> mouse_capture_; | 
 |   id __strong local_keyboard_monitor_ = nil; | 
 | }; | 
 |  | 
 | EventCaptureMac::EventCaptureMac(ui::EventHandler* event_handler, | 
 |                                  base::OnceClosure capture_lost_callback, | 
 |                                  gfx::NativeView web_contents_view, | 
 |                                  gfx::NativeWindow target_native_window) | 
 |     : mouse_capture_delegate_impl_(std::make_unique<MouseCaptureDelegateImpl>( | 
 |           event_handler, | 
 |           std::move(capture_lost_callback), | 
 |           web_contents_view, | 
 |           target_native_window)) { | 
 |   CreateKeyDownLocalMonitor(event_handler, target_native_window); | 
 | } | 
 |  | 
 | void EventCaptureMac::CreateKeyDownLocalMonitor( | 
 |     ui::EventHandler* event_handler, | 
 |     gfx::NativeWindow target_native_window) { | 
 |   DCHECK(event_handler); | 
 |   NSWindow* target_window = target_native_window.GetNativeNSWindow(); | 
 |  | 
 |   // Capture a WeakPtr. This allows the block to detect another event monitor | 
 |   // for the same event deleting |this|. | 
 |   base::WeakPtr<EventCaptureMac> weak_ptr = factory_.GetWeakPtr(); | 
 |  | 
 |   auto block = ^NSEvent*(NSEvent* event) { | 
 |     if (!weak_ptr) { | 
 |       return event; | 
 |     } | 
 |  | 
 |     if (!target_window || event.window == target_window) { | 
 |       std::unique_ptr<ui::Event> ui_event = | 
 |           ui::EventFromNative(base::apple::OwnedNSEvent(event)); | 
 |       if (!ui_event) { | 
 |         return event; | 
 |       } | 
 |       ui::EventType type = ui_event->type(); | 
 |       if (type == ui::EventType::kKeyPressed) { | 
 |         event_handler->OnKeyEvent(ui_event->AsKeyEvent()); | 
 |       } | 
 |       // Consume the event if allowed and the corresponding EventHandler method | 
 |       // requested. | 
 |       if (ui_event->cancelable() && ui_event->handled()) { | 
 |         return nil; | 
 |       } | 
 |     } | 
 |     return event; | 
 |   }; | 
 |  | 
 |   mouse_capture_delegate_impl_->SetKeyboardMonitor([NSEvent | 
 |       addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown | 
 |                                    handler:block]); | 
 | } | 
 |  | 
 | EventCaptureMac::~EventCaptureMac() { | 
 |   mouse_capture_delegate_impl_->Reset(); | 
 | } | 
 |  | 
 | void EventCaptureMac::SetCrossCursor() { | 
 |   [NSCursor.crosshairCursor set]; | 
 | } | 
 |  | 
 | }  // namespace image_editor |