• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
18PopupMenuHelper::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
25void 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
78void 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