Home | History | Annotate | Download | only in tab_contents
      1 // Copyright (c) 2011 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 #include "chrome/browser/tab_contents/popup_menu_helper_mac.h"
      8 
      9 #include "base/memory/scoped_nsobject.h"
     10 #include "base/message_loop.h"
     11 #include "chrome/browser/renderer_host/render_widget_host_view_mac.h"
     12 #import "chrome/browser/ui/cocoa/base_view.h"
     13 #include "content/browser/renderer_host/render_view_host.h"
     14 #import "content/common/chrome_application_mac.h"
     15 #include "content/common/notification_source.h"
     16 #include "webkit/glue/webmenurunner_mac.h"
     17 
     18 PopupMenuHelper::PopupMenuHelper(RenderViewHost* render_view_host)
     19     : render_view_host_(render_view_host) {
     20   notification_registrar_.Add(
     21       this, NotificationType::RENDER_WIDGET_HOST_DESTROYED,
     22       Source<RenderWidgetHost>(render_view_host));
     23 }
     24 
     25 void PopupMenuHelper::ShowPopupMenu(
     26     const gfx::Rect& bounds,
     27     int item_height,
     28     double item_font_size,
     29     int selected_item,
     30     const std::vector<WebMenuItem>& items,
     31     bool right_aligned) {
     32   // Retain the Cocoa view for the duration of the pop-up so that it can't be
     33   // dealloced if my Destroy() method is called while the pop-up's up (which
     34   // would in turn delete me, causing a crash once the -runMenuInView
     35   // call returns. That's what was happening in <http://crbug.com/33250>).
     36   RenderWidgetHostViewMac* rwhvm =
     37       static_cast<RenderWidgetHostViewMac*>(render_view_host_->view());
     38   scoped_nsobject<RenderWidgetHostViewCocoa> cocoa_view
     39       ([rwhvm->native_view() retain]);
     40 
     41   // Display the menu.
     42   scoped_nsobject<WebMenuRunner> menu_runner;
     43   menu_runner.reset([[WebMenuRunner alloc] initWithItems:items
     44                                                 fontSize:item_font_size
     45                                             rightAligned:right_aligned]);
     46 
     47   {
     48     // Make sure events can be pumped while the menu is up.
     49     MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
     50 
     51     // One of the events that could be pumped is |window.close()|.
     52     // User-initiated event-tracking loops protect against this by
     53     // setting flags in -[CrApplication sendEvent:], but since
     54     // web-content menus are initiated by IPC message the setup has to
     55     // be done manually.
     56     chrome_application_mac::ScopedSendingEvent sendingEventScoper;
     57 
     58     // Now run a SYNCHRONOUS NESTED EVENT LOOP until the pop-up is finished.
     59     [menu_runner runMenuInView:cocoa_view
     60                     withBounds:[cocoa_view flipRectToNSRect:bounds]
     61                   initialIndex:selected_item];
     62   }
     63 
     64   if (!render_view_host_) {
     65     // Bad news, the RenderViewHost got deleted while we were off running the
     66     // menu. Nothing to do.
     67     return;
     68   }
     69 
     70   if ([menu_runner menuItemWasChosen]) {
     71     render_view_host_->DidSelectPopupMenuItem(
     72         [menu_runner indexOfSelectedItem]);
     73   } else {
     74     render_view_host_->DidCancelPopupMenu();
     75   }
     76 }
     77 
     78 void PopupMenuHelper::Observe(
     79     NotificationType type,
     80     const NotificationSource& source,
     81     const NotificationDetails& details) {
     82   DCHECK(type == NotificationType::RENDER_WIDGET_HOST_DESTROYED);
     83   RenderViewHost* rvh = Source<RenderViewHost>(source).ptr();
     84   DCHECK_EQ(render_view_host_, rvh);
     85   render_view_host_ = NULL;
     86 }
     87 
     88