// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/base/cocoa/bubble_closer.h" #import namespace ui { BubbleCloser::BubbleCloser(gfx::NativeWindow window, base::RepeatingClosure on_click_outside) : on_click_outside_(std::move(on_click_outside)), factory_(this) { // Capture a WeakPtr via NSObject. This allows the block to detect another // event monitor for the same event deleting |this|. WeakPtrNSObject* handle = factory_.handle(); // Note that |window| will be retained when captured by the block below. // |this| is captured, but not retained. auto block = ^NSEvent*(NSEvent* event) { NSWindow* event_window = [event window]; if ([event_window isSheet]) return event; // Do not close the bubble if the event happened on a window with a // higher level. For example, the content of a browser action bubble // opens a calendar picker window with NSPopUpMenuWindowLevel, and a // date selection closes the picker window, but it should not close // the bubble. if ([event_window level] > [window level]) return event; // If the event is in |window|'s hierarchy, do not close the bubble. NSWindow* ancestor = event_window; while (ancestor) { if (ancestor == window) return event; ancestor = [ancestor parentWindow]; } if (BubbleCloser* owner = WeakPtrNSObjectFactory::Get(handle)) owner->OnClickOutside(); return event; }; event_tap_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSLeftMouseDownMask | NSRightMouseDownMask handler:block]; } BubbleCloser::~BubbleCloser() { [NSEvent removeMonitor:event_tap_]; } void BubbleCloser::OnClickOutside() { on_click_outside_.Run(); // Note: May delete |this|. } } // namespace ui