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