Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / ui / views / extensions / extension_popup.cc
blob566858620dd3e837200f8332096742344a5f33f2
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
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/extensions/extension_popup.h"
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/devtools/devtools_window.h"
11 #include "chrome/browser/extensions/extension_view_host.h"
12 #include "chrome/browser/extensions/extension_view_host_factory.h"
13 #include "chrome/browser/platform_util.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_window.h"
17 #include "chrome/browser/ui/host_desktop.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/browser/ui/views/frame/browser_view.h"
20 #include "content/public/browser/devtools_agent_host.h"
21 #include "content/public/browser/devtools_manager.h"
22 #include "content/public/browser/notification_details.h"
23 #include "content/public/browser/notification_source.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/web_contents.h"
26 #include "ui/gfx/insets.h"
27 #include "ui/views/layout/fill_layout.h"
28 #include "ui/views/widget/widget.h"
30 #if defined(USE_AURA)
31 #include "ui/aura/window.h"
32 #include "ui/wm/core/window_animations.h"
33 #include "ui/wm/core/window_util.h"
34 #include "ui/wm/public/activation_client.h"
35 #endif
37 #if defined(OS_WIN)
38 #include "ui/views/win/hwnd_util.h"
39 #endif
41 using content::BrowserContext;
42 using content::RenderViewHost;
43 using content::WebContents;
45 namespace {
47 // Returns true if |possible_owner| is the owner of |child|.
48 bool IsOwnerOf(gfx::NativeView child, gfx::NativeView possible_owner) {
49 if (!child)
50 return false;
51 #if defined(OS_WIN)
52 if (::GetWindow(views::HWNDForNativeView(child), GW_OWNER) ==
53 views::HWNDForNativeView(possible_owner))
54 return true;
55 #endif
56 return false;
59 } // namespace
61 // The minimum/maximum dimensions of the popup.
62 // The minimum is just a little larger than the size of the button itself.
63 // The maximum is an arbitrary number that should be smaller than most screens.
64 const int ExtensionPopup::kMinWidth = 25;
65 const int ExtensionPopup::kMinHeight = 25;
66 const int ExtensionPopup::kMaxWidth = 800;
67 const int ExtensionPopup::kMaxHeight = 600;
69 ExtensionPopup::ExtensionPopup(extensions::ExtensionViewHost* host,
70 views::View* anchor_view,
71 views::BubbleBorder::Arrow arrow,
72 ShowAction show_action)
73 : BubbleDelegateView(anchor_view, arrow),
74 host_(host),
75 devtools_callback_(base::Bind(
76 &ExtensionPopup::OnDevToolsStateChanged, base::Unretained(this))) {
77 inspect_with_devtools_ = show_action == SHOW_AND_INSPECT;
78 // Adjust the margin so that contents fit better.
79 const int margin = views::BubbleBorder::GetCornerRadius() / 2;
80 set_margins(gfx::Insets(margin, margin, margin, margin));
81 SetLayoutManager(new views::FillLayout());
82 AddChildView(host->view());
83 host->view()->set_container(this);
84 // Use OnNativeFocusChange to check for child window activation on deactivate.
85 set_close_on_deactivate(false);
87 // Wait to show the popup until the contained host finishes loading.
88 registrar_.Add(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
89 content::Source<WebContents>(host->host_contents()));
91 // Listen for the containing view calling window.close();
92 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
93 content::Source<BrowserContext>(host->browser_context()));
94 content::DevToolsManager::GetInstance()->AddAgentStateCallback(
95 devtools_callback_);
97 host_->view()->browser()->tab_strip_model()->AddObserver(this);
100 ExtensionPopup::~ExtensionPopup() {
101 content::DevToolsManager::GetInstance()->RemoveAgentStateCallback(
102 devtools_callback_);
104 host_->view()->browser()->tab_strip_model()->RemoveObserver(this);
107 void ExtensionPopup::Observe(int type,
108 const content::NotificationSource& source,
109 const content::NotificationDetails& details) {
110 switch (type) {
111 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME:
112 DCHECK(content::Source<WebContents>(host()->host_contents()) == source);
113 // Show when the content finishes loading and its width is computed.
114 ShowBubble();
115 break;
116 case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE:
117 // If we aren't the host of the popup, then disregard the notification.
118 if (content::Details<extensions::ExtensionHost>(host()) == details)
119 GetWidget()->Close();
120 break;
121 default:
122 NOTREACHED() << L"Received unexpected notification";
126 void ExtensionPopup::OnDevToolsStateChanged(
127 content::DevToolsAgentHost* agent_host, bool attached) {
128 // First check that the devtools are being opened on this popup.
129 if (host()->render_view_host() != agent_host->GetRenderViewHost())
130 return;
132 if (attached) {
133 // Set inspect_with_devtools_ so the popup will be kept open while
134 // the devtools are open.
135 inspect_with_devtools_ = true;
136 } else {
137 // Widget::Close posts a task, which should give the devtools window a
138 // chance to finish detaching from the inspected RenderViewHost.
139 GetWidget()->Close();
143 void ExtensionPopup::OnExtensionSizeChanged(ExtensionViewViews* view) {
144 SizeToContents();
147 gfx::Size ExtensionPopup::GetPreferredSize() {
148 // Constrain the size to popup min/max.
149 gfx::Size sz = views::View::GetPreferredSize();
150 sz.set_width(std::max(kMinWidth, std::min(kMaxWidth, sz.width())));
151 sz.set_height(std::max(kMinHeight, std::min(kMaxHeight, sz.height())));
152 return sz;
155 void ExtensionPopup::OnWidgetDestroying(views::Widget* widget) {
156 BubbleDelegateView::OnWidgetDestroying(widget);
157 #if defined(USE_AURA)
158 aura::Window* bubble_window = GetWidget()->GetNativeWindow();
159 aura::client::ActivationClient* activation_client =
160 aura::client::GetActivationClient(bubble_window->GetRootWindow());
161 activation_client->RemoveObserver(this);
162 #endif
165 void ExtensionPopup::OnWidgetActivationChanged(views::Widget* widget,
166 bool active) {
167 // Dismiss only if the window being activated is not owned by this popup's
168 // window. In particular, don't dismiss when we lose activation to a child
169 // dialog box. Possibly relevant: http://crbug.com/106723 and
170 // http://crbug.com/179786
171 views::Widget* this_widget = GetWidget();
173 // TODO(msw): Resolve crashes and remove checks. See: http://crbug.com/327776
174 CHECK(!close_on_deactivate());
175 CHECK(this_widget);
176 CHECK(widget);
178 gfx::NativeView activated_view = widget->GetNativeView();
179 gfx::NativeView this_view = this_widget->GetNativeView();
180 if (active && !inspect_with_devtools_ && activated_view != this_view &&
181 !IsOwnerOf(activated_view, this_view))
182 this_widget->Close();
185 #if defined(USE_AURA)
186 void ExtensionPopup::OnWindowActivated(aura::Window* gained_active,
187 aura::Window* lost_active) {
188 // DesktopNativeWidgetAura does not trigger the expected browser widget
189 // [de]activation events when activating widgets in its own root window.
190 // This additional check handles those cases. See: http://crbug.com/320889
191 aura::Window* this_window = GetWidget()->GetNativeWindow();
192 aura::Window* anchor_window = anchor_widget()->GetNativeWindow();
193 chrome::HostDesktopType host_desktop_type =
194 chrome::GetHostDesktopTypeForNativeWindow(this_window);
195 if (!inspect_with_devtools_ && anchor_window == gained_active &&
196 host_desktop_type != chrome::HOST_DESKTOP_TYPE_ASH &&
197 this_window->GetRootWindow() == anchor_window->GetRootWindow() &&
198 wm::GetTransientParent(gained_active) != this_window)
199 GetWidget()->Close();
201 #endif
203 void ExtensionPopup::ActiveTabChanged(content::WebContents* old_contents,
204 content::WebContents* new_contents,
205 int index,
206 int reason) {
207 GetWidget()->Close();
210 // static
211 ExtensionPopup* ExtensionPopup::ShowPopup(const GURL& url,
212 Browser* browser,
213 views::View* anchor_view,
214 views::BubbleBorder::Arrow arrow,
215 ShowAction show_action) {
216 extensions::ExtensionViewHost* host =
217 extensions::ExtensionViewHostFactory::CreatePopupHost(url, browser);
218 ExtensionPopup* popup = new ExtensionPopup(host, anchor_view, arrow,
219 show_action);
220 views::BubbleDelegateView::CreateBubble(popup);
222 #if defined(USE_AURA)
223 gfx::NativeView native_view = popup->GetWidget()->GetNativeView();
224 wm::SetWindowVisibilityAnimationType(
225 native_view,
226 wm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL);
227 wm::SetWindowVisibilityAnimationVerticalPosition(
228 native_view,
229 -3.0f);
230 #endif
232 // If the host had somehow finished loading, then we'd miss the notification
233 // and not show. This seems to happen in single-process mode.
234 if (host->did_stop_loading())
235 popup->ShowBubble();
237 #if defined(USE_AURA)
238 aura::Window* bubble_window = popup->GetWidget()->GetNativeWindow();
239 aura::client::ActivationClient* activation_client =
240 aura::client::GetActivationClient(bubble_window->GetRootWindow());
241 activation_client->AddObserver(popup);
242 #endif
244 return popup;
247 void ExtensionPopup::ShowBubble() {
248 GetWidget()->Show();
250 // Focus on the host contents when the bubble is first shown.
251 host()->host_contents()->Focus();
253 if (inspect_with_devtools_) {
254 DevToolsWindow::OpenDevToolsWindow(host()->render_view_host(),
255 DevToolsToggleAction::ShowConsole());