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