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 #include "chrome/browser/ui/views/extensions/extension_popup.h"
6
7 #include <vector>
8
9 #include "chrome/browser/debugger/devtools_manager.h"
10 #include "chrome/browser/debugger/devtools_toggle_action.h"
11 #include "chrome/browser/extensions/extension_host.h"
12 #include "chrome/browser/extensions/extension_process_manager.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_window.h"
16 #include "chrome/browser/ui/views/frame/browser_view.h"
17 #include "chrome/common/extensions/extension.h"
18 #include "content/browser/renderer_host/render_view_host.h"
19 #include "content/browser/renderer_host/render_widget_host_view.h"
20 #include "content/common/notification_details.h"
21 #include "content/common/notification_source.h"
22 #include "content/common/notification_type.h"
23 #include "views/widget/root_view.h"
24 #include "views/window/window.h"
25
26 #if defined(OS_LINUX)
27 #include "views/widget/widget_gtk.h"
28 #endif
29
30 #if defined(OS_CHROMEOS)
31 #include "chrome/browser/chromeos/wm_ipc.h"
32 #include "third_party/cros/chromeos_wm_ipc_enums.h"
33 #endif
34
35 using std::vector;
36 using views::Widget;
37
38 // The minimum/maximum dimensions of the popup.
39 // The minimum is just a little larger than the size of the button itself.
40 // The maximum is an arbitrary number that should be smaller than most screens.
41 const int ExtensionPopup::kMinWidth = 25;
42 const int ExtensionPopup::kMinHeight = 25;
43 const int ExtensionPopup::kMaxWidth = 800;
44 const int ExtensionPopup::kMaxHeight = 600;
45
ExtensionPopup(ExtensionHost * host,views::Widget * frame,const gfx::Rect & relative_to,BubbleBorder::ArrowLocation arrow_location,bool inspect_with_devtools,Observer * observer)46 ExtensionPopup::ExtensionPopup(ExtensionHost* host,
47 views::Widget* frame,
48 const gfx::Rect& relative_to,
49 BubbleBorder::ArrowLocation arrow_location,
50 bool inspect_with_devtools,
51 Observer* observer)
52 : BrowserBubble(host->view(),
53 frame,
54 relative_to,
55 arrow_location),
56 relative_to_(relative_to),
57 extension_host_(host),
58 inspect_with_devtools_(inspect_with_devtools),
59 close_on_lost_focus_(true),
60 closing_(false),
61 observer_(observer) {
62 AddRef(); // Balanced in Close();
63 set_delegate(this);
64 host->view()->SetContainer(this);
65
66 // We wait to show the popup until the contained host finishes loading.
67 registrar_.Add(this,
68 NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
69 Source<Profile>(host->profile()));
70
71 // Listen for the containing view calling window.close();
72 registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE,
73 Source<Profile>(host->profile()));
74 }
75
~ExtensionPopup()76 ExtensionPopup::~ExtensionPopup() {
77 }
78
Show(bool activate)79 void ExtensionPopup::Show(bool activate) {
80 if (visible())
81 return;
82
83 #if defined(OS_WIN)
84 frame_->GetWindow()->DisableInactiveRendering();
85 #endif
86
87 ResizeToView();
88 BrowserBubble::Show(activate);
89 }
90
BubbleBrowserWindowMoved(BrowserBubble * bubble)91 void ExtensionPopup::BubbleBrowserWindowMoved(BrowserBubble* bubble) {
92 ResizeToView();
93 }
94
BubbleBrowserWindowClosing(BrowserBubble * bubble)95 void ExtensionPopup::BubbleBrowserWindowClosing(BrowserBubble* bubble) {
96 if (!closing_)
97 Close();
98 }
99
BubbleGotFocus(BrowserBubble * bubble)100 void ExtensionPopup::BubbleGotFocus(BrowserBubble* bubble) {
101 // Forward the focus to the renderer.
102 host()->render_view_host()->view()->Focus();
103 }
104
BubbleLostFocus(BrowserBubble * bubble,bool lost_focus_to_child)105 void ExtensionPopup::BubbleLostFocus(BrowserBubble* bubble,
106 bool lost_focus_to_child) {
107 if (closing_ || // We are already closing.
108 inspect_with_devtools_ || // The popup is being inspected.
109 !close_on_lost_focus_ || // Our client is handling focus listening.
110 lost_focus_to_child) // A child of this view got focus.
111 return;
112
113 // When we do close on BubbleLostFocus, we do it in the next event loop
114 // because a subsequent event in this loop may also want to close this popup
115 // and if so, we want to allow that. Example: Clicking the same browser
116 // action button that opened the popup. If we closed immediately, the
117 // browser action container would fail to discover that the same button
118 // was pressed.
119 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(this,
120 &ExtensionPopup::Close));
121 }
122
123
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)124 void ExtensionPopup::Observe(NotificationType type,
125 const NotificationSource& source,
126 const NotificationDetails& details) {
127 switch (type.value) {
128 case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
129 // Once we receive did stop loading, the content will be complete and
130 // the width will have been computed. Now it's safe to show.
131 if (extension_host_.get() == Details<ExtensionHost>(details).ptr()) {
132 Show(true);
133
134 if (inspect_with_devtools_) {
135 // Listen for the the devtools window closing.
136 registrar_.Add(this, NotificationType::DEVTOOLS_WINDOW_CLOSING,
137 Source<Profile>(extension_host_->profile()));
138 DevToolsManager::GetInstance()->ToggleDevToolsWindow(
139 extension_host_->render_view_host(),
140 DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE);
141 }
142 }
143 break;
144 case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE:
145 // If we aren't the host of the popup, then disregard the notification.
146 if (Details<ExtensionHost>(host()) != details)
147 return;
148 Close();
149
150 break;
151 case NotificationType::DEVTOOLS_WINDOW_CLOSING:
152 // Make sure its the devtools window that inspecting our popup.
153 if (Details<RenderViewHost>(extension_host_->render_view_host()) !=
154 details)
155 return;
156
157 // If the devtools window is closing, we post a task to ourselves to
158 // close the popup. This gives the devtools window a chance to finish
159 // detaching from the inspected RenderViewHost.
160 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(this,
161 &ExtensionPopup::Close));
162
163 break;
164 default:
165 NOTREACHED() << L"Received unexpected notification";
166 }
167 }
168
OnExtensionPreferredSizeChanged(ExtensionView * view)169 void ExtensionPopup::OnExtensionPreferredSizeChanged(ExtensionView* view) {
170 // Constrain the size to popup min/max.
171 gfx::Size sz = view->GetPreferredSize();
172 view->SetBounds(view->x(), view->y(),
173 std::max(kMinWidth, std::min(kMaxWidth, sz.width())),
174 std::max(kMinHeight, std::min(kMaxHeight, sz.height())));
175
176 ResizeToView();
177 }
178
179 // static
Show(const GURL & url,Browser * browser,const gfx::Rect & relative_to,BubbleBorder::ArrowLocation arrow_location,bool inspect_with_devtools,Observer * observer)180 ExtensionPopup* ExtensionPopup::Show(
181 const GURL& url,
182 Browser* browser,
183 const gfx::Rect& relative_to,
184 BubbleBorder::ArrowLocation arrow_location,
185 bool inspect_with_devtools,
186 Observer* observer) {
187 ExtensionProcessManager* manager =
188 browser->profile()->GetExtensionProcessManager();
189 DCHECK(manager);
190 if (!manager)
191 return NULL;
192
193 ExtensionHost* host = manager->CreatePopup(url, browser);
194 views::Widget* frame = BrowserView::GetBrowserViewForNativeWindow(
195 browser->window()->GetNativeHandle())->GetWidget();
196 ExtensionPopup* popup = new ExtensionPopup(host, frame, relative_to,
197 arrow_location,
198 inspect_with_devtools, observer);
199
200 // If the host had somehow finished loading, then we'd miss the notification
201 // and not show. This seems to happen in single-process mode.
202 if (host->did_stop_loading())
203 popup->Show(true);
204
205 return popup;
206 }
207
Close()208 void ExtensionPopup::Close() {
209 if (closing_)
210 return;
211 closing_ = true;
212 DetachFromBrowser();
213
214 if (observer_)
215 observer_->ExtensionPopupIsClosing(this);
216
217 Release(); // Balanced in ctor.
218 }
219