• 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 #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