1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <Carbon/Carbon.h>
#include "content/browser/frame_host/popup_menu_helper_mac.h"
#include "base/mac/scoped_nsobject.h"
#import "base/mac/scoped_sending_event.h"
#include "base/message_loop/message_loop.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_mac.h"
#include "content/browser/renderer_host/webmenurunner_mac.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#import "ui/base/cocoa/base_view.h"
namespace content {
namespace {
bool g_allow_showing_popup_menus = true;
} // namespace
PopupMenuHelper::PopupMenuHelper(RenderFrameHost* render_frame_host)
: render_frame_host_(static_cast<RenderFrameHostImpl*>(render_frame_host)),
menu_runner_(nil),
popup_was_hidden_(false) {
notification_registrar_.Add(
this, NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
Source<RenderWidgetHost>(render_frame_host->GetRenderViewHost()));
notification_registrar_.Add(
this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
Source<RenderWidgetHost>(render_frame_host->GetRenderViewHost()));
}
void PopupMenuHelper::ShowPopupMenu(
const gfx::Rect& bounds,
int item_height,
double item_font_size,
int selected_item,
const std::vector<MenuItem>& items,
bool right_aligned,
bool allow_multiple_selection) {
// Only single selection list boxes show a popup on Mac.
DCHECK(!allow_multiple_selection);
if (!g_allow_showing_popup_menus)
return;
// Retain the Cocoa view for the duration of the pop-up so that it can't be
// dealloced if my Destroy() method is called while the pop-up's up (which
// would in turn delete me, causing a crash once the -runMenuInView
// call returns. That's what was happening in <http://crbug.com/33250>).
RenderWidgetHostViewMac* rwhvm =
static_cast<RenderWidgetHostViewMac*>(GetRenderWidgetHostView());
base::scoped_nsobject<RenderWidgetHostViewCocoa> cocoa_view(
[rwhvm->cocoa_view() retain]);
// Display the menu.
menu_runner_ = [[WebMenuRunner alloc] initWithItems:items
fontSize:item_font_size
rightAligned:right_aligned];
{
// Make sure events can be pumped while the menu is up.
base::MessageLoop::ScopedNestableTaskAllower allow(
base::MessageLoop::current());
// One of the events that could be pumped is |window.close()|.
// User-initiated event-tracking loops protect against this by
// setting flags in -[CrApplication sendEvent:], but since
// web-content menus are initiated by IPC message the setup has to
// be done manually.
base::mac::ScopedSendingEvent sending_event_scoper;
// Now run a SYNCHRONOUS NESTED EVENT LOOP until the pop-up is finished.
[menu_runner_ runMenuInView:cocoa_view
withBounds:[cocoa_view flipRectToNSRect:bounds]
initialIndex:selected_item];
}
if (!render_frame_host_) {
// Bad news, the RenderFrameHost got deleted while we were off running the
// menu. Nothing to do.
[menu_runner_ release];
menu_runner_ = nil;
return;
}
if (!popup_was_hidden_) {
if ([menu_runner_ menuItemWasChosen]) {
render_frame_host_->DidSelectPopupMenuItem(
[menu_runner_ indexOfSelectedItem]);
} else {
render_frame_host_->DidCancelPopupMenu();
}
}
[menu_runner_ release];
menu_runner_ = nil;
}
void PopupMenuHelper::Hide() {
if (menu_runner_)
[menu_runner_ hide];
popup_was_hidden_ = true;
}
// static
void PopupMenuHelper::DontShowPopupMenuForTesting() {
g_allow_showing_popup_menus = false;
}
RenderWidgetHostViewMac* PopupMenuHelper::GetRenderWidgetHostView() const {
return static_cast<RenderWidgetHostViewMac*>(
render_frame_host_->GetRenderViewHost()->GetView());
}
void PopupMenuHelper::Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) {
DCHECK_EQ(Source<RenderWidgetHost>(source).ptr(),
render_frame_host_->GetRenderViewHost());
switch (type) {
case NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED: {
render_frame_host_ = NULL;
break;
}
case NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: {
bool is_visible = *Details<bool>(details).ptr();
if (!is_visible)
Hide();
break;
}
}
}
} // namespace content
|