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