web_contents_view_mac.mm revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#import <Carbon/Carbon.h>
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#import "content/browser/web_contents/web_contents_view_mac.h"
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <string>
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#import "base/mac/scoped_sending_event.h"
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/message_loop.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#import "base/message_pump_mac.h"
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/browser/renderer_host/popup_menu_helper_mac.h"
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/browser/renderer_host/render_view_host_factory.h"
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/browser/renderer_host/render_view_host_impl.h"
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/browser/renderer_host/render_widget_host_view_mac.h"
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/browser/web_contents/web_contents_impl.h"
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#import "content/browser/web_contents/web_drag_dest_mac.h"
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#import "content/browser/web_contents/web_drag_source_mac.h"
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/common/view_messages.h"
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/public/browser/web_contents_delegate.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/web_contents_view_delegate.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "skia/ext/skia_utils_mac.h"
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#import "third_party/mozilla/NSPasteboard+Utils.h"
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/base/clipboard/custom_data_helper.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#import "ui/base/cocoa/focus_tracker.h"
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/base/dragdrop/cocoa_dnd_util.h"
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/image/image_skia_util_mac.h"
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using WebKit::WebDragOperation;
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using WebKit::WebDragOperationsMask;
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::PopupMenuHelper;
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::RenderViewHostFactory;
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::RenderWidgetHostView;
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::RenderWidgetHostViewMac;
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::WebContents;
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::WebContentsImpl;
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)using content::WebContentsViewMac;
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Ensure that the WebKit::WebDragOperation enum values stay in sync with
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// NSDragOperation constants, since the code below static_casts between 'em.
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#define COMPILE_ASSERT_MATCHING_ENUM(name) \
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  COMPILE_ASSERT(int(NS##name) == int(WebKit::Web##name), enum_mismatch_##name)
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)COMPILE_ASSERT_MATCHING_ENUM(DragOperationNone);
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)COMPILE_ASSERT_MATCHING_ENUM(DragOperationCopy);
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)COMPILE_ASSERT_MATCHING_ENUM(DragOperationLink);
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)COMPILE_ASSERT_MATCHING_ENUM(DragOperationGeneric);
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)COMPILE_ASSERT_MATCHING_ENUM(DragOperationPrivate);
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)COMPILE_ASSERT_MATCHING_ENUM(DragOperationMove);
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)COMPILE_ASSERT_MATCHING_ENUM(DragOperationDelete);
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)COMPILE_ASSERT_MATCHING_ENUM(DragOperationEvery);
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)@interface WebContentsViewCocoa (Private)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (id)initWithWebContentsViewMac:(WebContentsViewMac*)w;
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (void)registerDragTypes;
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (void)setCurrentDragOperation:(NSDragOperation)operation;
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (WebDropData*)dropData;
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)- (void)startDragWithDropData:(const WebDropData&)dropData
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            dragOperationMask:(NSDragOperation)operationMask
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                        image:(NSImage*)image
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                       offset:(NSPoint)offset;
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)- (void)cancelDeferredClose;
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)- (void)clearWebContentsView;
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)- (void)closeTabAfterEvent;
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)- (void)viewDidBecomeFirstResponder:(NSNotification*)notification;
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)@end
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace content {
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)WebContentsViewPort* CreateWebContentsView(
71424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    WebContentsImpl* web_contents,
72424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    WebContentsViewDelegate* delegate,
73424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    RenderViewHostDelegateView** render_view_host_delegate_view) {
74424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  WebContentsViewMac* rv = new WebContentsViewMac(web_contents, delegate);
75424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  *render_view_host_delegate_view = rv;
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return rv;
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)WebContentsViewMac::WebContentsViewMac(WebContentsImpl* web_contents,
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       WebContentsViewDelegate* delegate)
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : web_contents_(web_contents),
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      delegate_(delegate),
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      allow_overlapping_views_(false) {
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)WebContentsViewMac::~WebContentsViewMac() {
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // This handles the case where a renderer close call was deferred
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // while the user was operating a UI control which resulted in a
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // close.  In that case, the Cocoa view outlives the
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // WebContentsViewMac instance due to Cocoa retain count.
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  [cocoa_view_ cancelDeferredClose];
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  [cocoa_view_ clearWebContentsView];
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)gfx::NativeView WebContentsViewMac::GetNativeView() const {
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return cocoa_view_.get();
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
98
99gfx::NativeView WebContentsViewMac::GetContentNativeView() const {
100  RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
101  if (!rwhv)
102    return NULL;
103  return rwhv->GetNativeView();
104}
105
106gfx::NativeWindow WebContentsViewMac::GetTopLevelNativeWindow() const {
107  return [cocoa_view_.get() window];
108}
109
110void WebContentsViewMac::GetContainerBounds(gfx::Rect* out) const {
111  // Convert bounds to window coordinate space.
112  NSRect bounds =
113      [cocoa_view_.get() convertRect:[cocoa_view_.get() bounds] toView:nil];
114
115  // Convert bounds to screen coordinate space.
116  NSWindow* window = [cocoa_view_.get() window];
117  bounds.origin = [window convertBaseToScreen:bounds.origin];
118
119  // Flip y to account for screen flip.
120  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
121  bounds.origin.y = [screen frame].size.height - bounds.origin.y
122      - bounds.size.height;
123  *out = gfx::Rect(NSRectToCGRect(bounds));
124}
125
126void WebContentsViewMac::StartDragging(
127    const WebDropData& drop_data,
128    WebDragOperationsMask allowed_operations,
129    const gfx::ImageSkia& image,
130    const gfx::Vector2d& image_offset,
131    const DragEventSourceInfo& event_info) {
132  // By allowing nested tasks, the code below also allows Close(),
133  // which would deallocate |this|.  The same problem can occur while
134  // processing -sendEvent:, so Close() is deferred in that case.
135  // Drags from web content do not come via -sendEvent:, this sets the
136  // same flag -sendEvent: would.
137  base::mac::ScopedSendingEvent sending_event_scoper;
138
139  // The drag invokes a nested event loop, arrange to continue
140  // processing events.
141  MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
142  NSDragOperation mask = static_cast<NSDragOperation>(allowed_operations);
143  NSPoint offset = NSPointFromCGPoint(
144      gfx::PointAtOffsetFromOrigin(image_offset).ToCGPoint());
145  [cocoa_view_ startDragWithDropData:drop_data
146                   dragOperationMask:mask
147                               image:gfx::NSImageFromImageSkia(image)
148                              offset:offset];
149}
150
151void WebContentsViewMac::OnTabCrashed(base::TerminationStatus /* status */,
152                                      int /* error_code */) {
153}
154
155void WebContentsViewMac::SizeContents(const gfx::Size& size) {
156  // TODO(brettw | japhet) This is a hack and should be removed.
157  // See web_contents_view.h.
158  gfx::Rect rect(gfx::Point(), size);
159  WebContentsViewCocoa* view = cocoa_view_.get();
160
161  NSPoint origin = [view frame].origin;
162  NSRect frame = [view flipRectToNSRect:rect];
163  frame.origin = NSMakePoint(NSMinX(frame) + origin.x,
164                             NSMinY(frame) + origin.y);
165  [view setFrame:frame];
166}
167
168void WebContentsViewMac::Focus() {
169  NSWindow* window = [cocoa_view_.get() window];
170  [window makeFirstResponder:GetContentNativeView()];
171  if (![window isVisible])
172    return;
173  [window makeKeyAndOrderFront:nil];
174}
175
176void WebContentsViewMac::SetInitialFocus() {
177  if (web_contents_->FocusLocationBarByDefault())
178    web_contents_->SetFocusToLocationBar(false);
179  else
180    [[cocoa_view_.get() window] makeFirstResponder:GetContentNativeView()];
181}
182
183void WebContentsViewMac::StoreFocus() {
184  // We're explicitly being asked to store focus, so don't worry if there's
185  // already a view saved.
186  focus_tracker_.reset(
187      [[FocusTracker alloc] initWithWindow:[cocoa_view_ window]]);
188}
189
190void WebContentsViewMac::RestoreFocus() {
191  // TODO(avi): Could we be restoring a view that's no longer in the key view
192  // chain?
193  if (!(focus_tracker_.get() &&
194        [focus_tracker_ restoreFocusInWindow:[cocoa_view_ window]])) {
195    // Fall back to the default focus behavior if we could not restore focus.
196    // TODO(shess): If location-bar gets focus by default, this will
197    // select-all in the field.  If there was a specific selection in
198    // the field when we navigated away from it, we should restore
199    // that selection.
200    SetInitialFocus();
201  }
202
203  focus_tracker_.reset(nil);
204}
205
206WebDropData* WebContentsViewMac::GetDropData() const {
207  return [cocoa_view_ dropData];
208}
209
210void WebContentsViewMac::UpdateDragCursor(WebDragOperation operation) {
211  [cocoa_view_ setCurrentDragOperation: operation];
212}
213
214void WebContentsViewMac::GotFocus() {
215  // This is only used in the views FocusManager stuff but it bleeds through
216  // all subclasses. http://crbug.com/21875
217}
218
219// This is called when the renderer asks us to take focus back (i.e., it has
220// iterated past the last focusable element on the page).
221void WebContentsViewMac::TakeFocus(bool reverse) {
222  if (reverse) {
223    [[cocoa_view_ window] selectPreviousKeyView:cocoa_view_.get()];
224  } else {
225    [[cocoa_view_ window] selectNextKeyView:cocoa_view_.get()];
226  }
227}
228
229void WebContentsViewMac::ShowContextMenu(const ContextMenuParams& params,
230                                         ContextMenuSourceType type) {
231  // Allow delegates to handle the context menu operation first.
232  if (web_contents_->GetDelegate() &&
233      web_contents_->GetDelegate()->HandleContextMenu(params)) {
234    return;
235  }
236
237  if (delegate())
238    delegate()->ShowContextMenu(params, type);
239  else
240    DLOG(ERROR) << "Cannot show context menus without a delegate.";
241}
242
243// Display a popup menu for WebKit using Cocoa widgets.
244void WebContentsViewMac::ShowPopupMenu(
245    const gfx::Rect& bounds,
246    int item_height,
247    double item_font_size,
248    int selected_item,
249    const std::vector<WebMenuItem>& items,
250    bool right_aligned,
251    bool allow_multiple_selection) {
252  PopupMenuHelper popup_menu_helper(web_contents_->GetRenderViewHost());
253  popup_menu_helper.ShowPopupMenu(bounds, item_height, item_font_size,
254                                  selected_item, items, right_aligned,
255                                  allow_multiple_selection);
256}
257
258gfx::Rect WebContentsViewMac::GetViewBounds() const {
259  // This method is not currently used on mac.
260  NOTIMPLEMENTED();
261  return gfx::Rect();
262}
263
264void WebContentsViewMac::SetAllowOverlappingViews(bool overlapping) {
265  if (allow_overlapping_views_ == overlapping)
266    return;
267
268  allow_overlapping_views_ = overlapping;
269  RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
270      web_contents_->GetRenderWidgetHostView());
271  if (view)
272    view->SetAllowOverlappingViews(allow_overlapping_views_);
273}
274
275void WebContentsViewMac::CreateView(
276    const gfx::Size& initial_size, gfx::NativeView context) {
277  WebContentsViewCocoa* view =
278      [[WebContentsViewCocoa alloc] initWithWebContentsViewMac:this];
279  cocoa_view_.reset(view);
280}
281
282RenderWidgetHostView* WebContentsViewMac::CreateViewForWidget(
283    RenderWidgetHost* render_widget_host) {
284  if (render_widget_host->GetView()) {
285    // During testing, the view will already be set up in most cases to the
286    // test view, so we don't want to clobber it with a real one. To verify that
287    // this actually is happening (and somebody isn't accidentally creating the
288    // view twice), we check for the RVH Factory, which will be set when we're
289    // making special ones (which go along with the special views).
290    DCHECK(RenderViewHostFactory::has_factory());
291    return render_widget_host->GetView();
292  }
293
294  RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
295      RenderWidgetHostView::CreateViewForWidget(render_widget_host));
296  if (delegate()) {
297    NSObject<RenderWidgetHostViewMacDelegate>* rw_delegate =
298        delegate()->CreateRenderWidgetHostViewDelegate(render_widget_host);
299    view->SetDelegate(rw_delegate);
300  }
301  view->SetAllowOverlappingViews(allow_overlapping_views_);
302
303  // Fancy layout comes later; for now just make it our size and resize it
304  // with us. In case there are other siblings of the content area, we want
305  // to make sure the content area is on the bottom so other things draw over
306  // it.
307  NSView* view_view = view->GetNativeView();
308  [view_view setFrame:[cocoa_view_.get() bounds]];
309  [view_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
310  // Add the new view below all other views; this also keeps it below any
311  // overlay view installed.
312  [cocoa_view_.get() addSubview:view_view
313                     positioned:NSWindowBelow
314                     relativeTo:nil];
315  // For some reason known only to Cocoa, the autorecalculation of the key view
316  // loop set on the window doesn't set the next key view when the subview is
317  // added. On 10.6 things magically work fine; on 10.5 they fail
318  // <http://crbug.com/61493>. Digging into Cocoa key view loop code yielded
319  // madness; TODO(avi,rohit): look at this again and figure out what's really
320  // going on.
321  [cocoa_view_.get() setNextKeyView:view_view];
322  return view;
323}
324
325RenderWidgetHostView* WebContentsViewMac::CreateViewForPopupWidget(
326    RenderWidgetHost* render_widget_host) {
327  return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host);
328}
329
330void WebContentsViewMac::SetPageTitle(const string16& title) {
331  // Meaningless on the Mac; widgets don't have a "title" attribute
332}
333
334
335void WebContentsViewMac::RenderViewCreated(RenderViewHost* host) {
336  // We want updates whenever the intrinsic width of the webpage changes.
337  // Put the RenderView into that mode. The preferred width is used for example
338  // when the "zoom" button in the browser window is clicked.
339  host->EnablePreferredSizeMode();
340}
341
342void WebContentsViewMac::RenderViewSwappedIn(RenderViewHost* host) {
343}
344
345void WebContentsViewMac::SetOverscrollControllerEnabled(bool enabled) {
346}
347
348bool WebContentsViewMac::IsEventTracking() const {
349  return base::MessagePumpMac::IsHandlingSendEvent();
350}
351
352// Arrange to call CloseTab() after we're back to the main event loop.
353// The obvious way to do this would be PostNonNestableTask(), but that
354// will fire when the event-tracking loop polls for events.  So we
355// need to bounce the message via Cocoa, instead.
356void WebContentsViewMac::CloseTabAfterEventTracking() {
357  [cocoa_view_ cancelDeferredClose];
358  [cocoa_view_ performSelector:@selector(closeTabAfterEvent)
359                    withObject:nil
360                    afterDelay:0.0];
361}
362
363void WebContentsViewMac::CloseTab() {
364  web_contents_->Close(web_contents_->GetRenderViewHost());
365}
366
367}  // namespace content
368
369@implementation WebContentsViewCocoa
370
371- (id)initWithWebContentsViewMac:(WebContentsViewMac*)w {
372  self = [super initWithFrame:NSZeroRect];
373  if (self != nil) {
374    webContentsView_ = w;
375    dragDest_.reset(
376        [[WebDragDest alloc] initWithWebContentsImpl:[self webContents]]);
377    [self registerDragTypes];
378
379    [[NSNotificationCenter defaultCenter]
380         addObserver:self
381            selector:@selector(viewDidBecomeFirstResponder:)
382                name:kViewDidBecomeFirstResponder
383              object:nil];
384
385    if (webContentsView_->delegate()) {
386      [dragDest_ setDragDelegate:webContentsView_->delegate()->
387          GetDragDestDelegate()];
388    }
389  }
390  return self;
391}
392
393- (void)dealloc {
394  // Cancel any deferred tab closes, just in case.
395  [self cancelDeferredClose];
396
397  // This probably isn't strictly necessary, but can't hurt.
398  [self unregisterDraggedTypes];
399
400  [[NSNotificationCenter defaultCenter] removeObserver:self];
401
402  [super dealloc];
403}
404
405// Registers for the view for the appropriate drag types.
406- (void)registerDragTypes {
407  NSArray* types = [NSArray arrayWithObjects:
408      ui::kChromeDragDummyPboardType,
409      kWebURLsWithTitlesPboardType,
410      NSURLPboardType,
411      NSStringPboardType,
412      NSHTMLPboardType,
413      NSRTFPboardType,
414      NSFilenamesPboardType,
415      ui::kWebCustomDataPboardType,
416      nil];
417  [self registerForDraggedTypes:types];
418}
419
420- (void)setCurrentDragOperation:(NSDragOperation)operation {
421  [dragDest_ setCurrentOperation:operation];
422}
423
424- (WebDropData*)dropData {
425  return [dragDest_ currentDropData];
426}
427
428- (WebContentsImpl*)webContents {
429  if (webContentsView_ == NULL)
430    return NULL;
431  return webContentsView_->web_contents();
432}
433
434- (void)mouseEvent:(NSEvent*)theEvent {
435  WebContentsImpl* webContents = [self webContents];
436  if (webContents && webContents->GetDelegate()) {
437    NSPoint location = [NSEvent mouseLocation];
438    if ([theEvent type] == NSMouseMoved)
439      webContents->GetDelegate()->ContentsMouseEvent(
440          webContents, gfx::Point(location.x, location.y), true);
441    if ([theEvent type] == NSMouseExited)
442      webContents->GetDelegate()->ContentsMouseEvent(
443          webContents, gfx::Point(location.x, location.y), false);
444  }
445}
446
447- (void)setMouseDownCanMoveWindow:(BOOL)canMove {
448  mouseDownCanMoveWindow_ = canMove;
449}
450
451- (BOOL)mouseDownCanMoveWindow {
452  // This is needed to prevent mouseDowns from moving the window
453  // around.  The default implementation returns YES only for opaque
454  // views.  WebContentsViewCocoa does not draw itself in any way, but
455  // its subviews do paint their entire frames.  Returning NO here
456  // saves us the effort of overriding this method in every possible
457  // subview.
458  return mouseDownCanMoveWindow_;
459}
460
461- (void)pasteboard:(NSPasteboard*)sender provideDataForType:(NSString*)type {
462  [dragSource_ lazyWriteToPasteboard:sender
463                             forType:type];
464}
465
466- (void)startDragWithDropData:(const WebDropData&)dropData
467            dragOperationMask:(NSDragOperation)operationMask
468                        image:(NSImage*)image
469                       offset:(NSPoint)offset {
470  dragSource_.reset([[WebDragSource alloc]
471      initWithContents:[self webContents]
472                  view:self
473              dropData:&dropData
474                 image:image
475                offset:offset
476            pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
477     dragOperationMask:operationMask]);
478  [dragSource_ startDrag];
479}
480
481// NSDraggingSource methods
482
483// Returns what kind of drag operations are available. This is a required
484// method for NSDraggingSource.
485- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
486  if (dragSource_.get())
487    return [dragSource_ draggingSourceOperationMaskForLocal:isLocal];
488  // No web drag source - this is the case for dragging a file from the
489  // downloads manager. Default to copy operation. Note: It is desirable to
490  // allow the user to either move or copy, but this requires additional
491  // plumbing to update the download item's path once its moved.
492  return NSDragOperationCopy;
493}
494
495// Called when a drag initiated in our view ends.
496- (void)draggedImage:(NSImage*)anImage
497             endedAt:(NSPoint)screenPoint
498           operation:(NSDragOperation)operation {
499  [dragSource_ endDragAt:screenPoint operation:operation];
500
501  // Might as well throw out this object now.
502  dragSource_.reset();
503}
504
505// Called when a drag initiated in our view moves.
506- (void)draggedImage:(NSImage*)draggedImage movedTo:(NSPoint)screenPoint {
507  [dragSource_ moveDragTo:screenPoint];
508}
509
510// Called when we're informed where a file should be dropped.
511- (NSArray*)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDest {
512  if (![dropDest isFileURL])
513    return nil;
514
515  NSString* file_name = [dragSource_ dragPromisedFileTo:[dropDest path]];
516  if (!file_name)
517    return nil;
518
519  return [NSArray arrayWithObject:file_name];
520}
521
522// NSDraggingDestination methods
523
524- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
525  return [dragDest_ draggingEntered:sender view:self];
526}
527
528- (void)draggingExited:(id<NSDraggingInfo>)sender {
529  [dragDest_ draggingExited:sender];
530}
531
532- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
533  return [dragDest_ draggingUpdated:sender view:self];
534}
535
536- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
537  return [dragDest_ performDragOperation:sender view:self];
538}
539
540- (void)cancelDeferredClose {
541  SEL aSel = @selector(closeTabAfterEvent);
542  [NSObject cancelPreviousPerformRequestsWithTarget:self
543                                           selector:aSel
544                                             object:nil];
545}
546
547- (void)clearWebContentsView {
548  webContentsView_ = NULL;
549  [dragSource_ clearWebContentsView];
550}
551
552- (void)closeTabAfterEvent {
553  webContentsView_->CloseTab();
554}
555
556- (void)viewDidBecomeFirstResponder:(NSNotification*)notification {
557  NSView* view = [notification object];
558  if (![[self subviews] containsObject:view])
559    return;
560
561  NSSelectionDirection direction =
562      [[[notification userInfo] objectForKey:kSelectionDirection]
563        unsignedIntegerValue];
564  if (direction == NSDirectSelection)
565    return;
566
567  [self webContents]->
568      FocusThroughTabTraversal(direction == NSSelectingPrevious);
569}
570
571@end
572