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