1// Copyright 2016 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#import "ui/views/cocoa/drag_drop_client_mac.h" 6 7#include "base/mac/mac_util.h" 8#include "base/run_loop.h" 9#include "base/strings/sys_string_conversions.h" 10#import "components/remote_cocoa/app_shim/bridged_content_view.h" 11#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" 12#import "ui/base/dragdrop/os_exchange_data_provider_mac.h" 13#include "ui/gfx/image/image_skia_util_mac.h" 14#include "ui/views/drag_utils.h" 15#include "ui/views/widget/native_widget_mac.h" 16 17namespace views { 18 19DragDropClientMac::DragDropClientMac( 20 remote_cocoa::NativeWidgetNSWindowBridge* bridge, 21 View* root_view) 22 : drop_helper_(root_view), bridge_(bridge) { 23 DCHECK(bridge); 24} 25 26DragDropClientMac::~DragDropClientMac() {} 27 28void DragDropClientMac::StartDragAndDrop( 29 View* view, 30 std::unique_ptr<ui::OSExchangeData> data, 31 int operation, 32 ui::DragDropTypes::DragEventSource source) { 33 exchange_data_ = std::move(data); 34 source_operation_ = operation; 35 is_drag_source_ = true; 36 37 const ui::OSExchangeDataProviderMac& provider_mac = 38 static_cast<const ui::OSExchangeDataProviderMac&>( 39 exchange_data_->provider()); 40 41 // Release capture before beginning the dragging session. Capture may have 42 // been acquired on the mouseDown, but capture is not required during the 43 // dragging session and the mouseUp that would release it will be suppressed. 44 bridge_->ReleaseCapture(); 45 46 // Synthesize an event for dragging, since we can't be sure that 47 // [NSApp currentEvent] will return a valid dragging event. 48 NSWindow* window = bridge_->ns_window(); 49 NSPoint position = [window mouseLocationOutsideOfEventStream]; 50 NSTimeInterval event_time = [[NSApp currentEvent] timestamp]; 51 NSEvent* event = [NSEvent mouseEventWithType:NSLeftMouseDragged 52 location:position 53 modifierFlags:NSLeftMouseDraggedMask 54 timestamp:event_time 55 windowNumber:[window windowNumber] 56 context:nil 57 eventNumber:0 58 clickCount:1 59 pressure:1.0]; 60 61 NSImage* image = gfx::NSImageFromImageSkiaWithColorSpace( 62 provider_mac.GetDragImage(), base::mac::GetSRGBColorSpace()); 63 64 DCHECK(!NSEqualSizes([image size], NSZeroSize)); 65 NSDraggingItem* drag_item = provider_mac.GetDraggingItem(); 66 67 // Subtract the image's height from the y location so that the mouse will be 68 // at the upper left corner of the image. 69 NSRect dragging_frame = 70 NSMakeRect([event locationInWindow].x, 71 [event locationInWindow].y - [image size].height, 72 [image size].width, [image size].height); 73 [drag_item setDraggingFrame:dragging_frame contents:image]; 74 75 [bridge_->ns_view() beginDraggingSessionWithItems:@[ drag_item ] 76 event:event 77 source:bridge_->ns_view()]; 78 79 // Since Drag and drop is asynchronous on Mac, we need to spin a nested run 80 // loop for consistency with other platforms. 81 base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); 82 quit_closure_ = run_loop.QuitClosure(); 83 run_loop.Run(); 84} 85 86NSDragOperation DragDropClientMac::DragUpdate(id<NSDraggingInfo> sender) { 87 if (!exchange_data_) { 88 exchange_data_ = std::make_unique<OSExchangeData>( 89 ui::OSExchangeDataProviderMac::CreateProviderWrappingPasteboard( 90 [sender draggingPasteboard])); 91 source_operation_ = ui::DragDropTypes::NSDragOperationToDragOperation( 92 [sender draggingSourceOperationMask]); 93 } 94 95 last_operation_ = drop_helper_.OnDragOver( 96 *exchange_data_, LocationInView([sender draggingLocation]), 97 source_operation_); 98 return ui::DragDropTypes::DragOperationToNSDragOperation(last_operation_); 99} 100 101NSDragOperation DragDropClientMac::Drop(id<NSDraggingInfo> sender) { 102 // OnDrop may delete |this|, so clear |exchange_data_| first. 103 std::unique_ptr<ui::OSExchangeData> exchange_data = std::move(exchange_data_); 104 105 int drag_operation = drop_helper_.OnDrop( 106 *exchange_data, LocationInView([sender draggingLocation]), 107 last_operation_); 108 return ui::DragDropTypes::DragOperationToNSDragOperation(drag_operation); 109} 110 111void DragDropClientMac::EndDrag() { 112 exchange_data_.reset(); 113 is_drag_source_ = false; 114 115 // Allow a test to invoke EndDrag() without spinning the nested run loop. 116 if (!quit_closure_.is_null()) 117 std::move(quit_closure_).Run(); 118} 119 120void DragDropClientMac::DragExit() { 121 drop_helper_.OnDragExit(); 122 if (!is_drag_source_) 123 exchange_data_.reset(); 124} 125 126gfx::Point DragDropClientMac::LocationInView(NSPoint point) const { 127 NSRect content_rect = [bridge_->ns_window() 128 contentRectForFrameRect:[bridge_->ns_window() frame]]; 129 return gfx::Point(point.x, NSHeight(content_rect) - point.y); 130} 131 132} // namespace views 133