Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / extensions / extension_popup.cc
blob184f82aca55dc50633be2d9c77305c6aca1ccd5c
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 "content/public/browser/web_contents_view.h"
27 #include "ui/gfx/insets.h"
28 #include "ui/views/layout/fill_layout.h"
29 #include "ui/views/widget/widget.h"
31 #if defined(USE_AURA)
32 #include "ui/aura/client/activation_client.h"
33 #include "ui/aura/window.h"
34 #include "ui/views/corewm/window_animations.h"
35 #include "ui/views/corewm/window_util.h"
36 #endif
38 #if defined(OS_WIN)
39 #include "ui/views/win/hwnd_util.h"
40 #endif
42 using content::BrowserContext;
43 using content::RenderViewHost;
44 using content::WebContents;
46 namespace {
48 // Returns true if |possible_owner| is the owner of |child|.
49 bool IsOwnerOf(gfx::NativeView child, gfx::NativeView possible_owner) {
50 if (!child)
51 return false;
52 #if defined(OS_WIN)
53 if (::GetWindow(views::HWNDForNativeView(child), GW_OWNER) ==
54 views::HWNDForNativeView(possible_owner))
55 return true;
56 #endif
57 return false;
60 } // namespace
62 // The minimum/maximum dimensions of the popup.
63 // The minimum is just a little larger than the size of the button itself.
64 // The maximum is an arbitrary number that should be smaller than most screens.
65 const int ExtensionPopup::kMinWidth = 25;
66 const int ExtensionPopup::kMinHeight = 25;
67 const int ExtensionPopup::kMaxWidth = 800;
68 const int ExtensionPopup::kMaxHeight = 600;
70 ExtensionPopup::ExtensionPopup(extensions::ExtensionViewHost* host,
71 views::View* anchor_view,
72 views::BubbleBorder::Arrow arrow,
73 ShowAction show_action)
74 : BubbleDelegateView(anchor_view, arrow),
75 host_(host),
76 devtools_callback_(base::Bind(
77 &ExtensionPopup::OnDevToolsStateChanged, base::Unretained(this))) {
78 inspect_with_devtools_ = show_action == SHOW_AND_INSPECT;
79 // Adjust the margin so that contents fit better.
80 const int margin = views::BubbleBorder::GetCornerRadius() / 2;
81 set_margins(gfx::Insets(margin, margin, margin, margin));
82 SetLayoutManager(new views::FillLayout());
83 AddChildView(host->view());
84 host->view()->set_container(this);
85 // Use OnNativeFocusChange to check for child window activation on deactivate.
86 set_close_on_deactivate(false);
87 // Make the bubble move with its anchor (during inspection, etc.).
88 set_move_with_anchor(true);
90 // Wait to show the popup until the contained host finishes loading.
91 registrar_.Add(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
92 content::Source<WebContents>(host->host_contents()));
94 // Listen for the containing view calling window.close();
95 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
96 content::Source<BrowserContext>(host->browser_context()));
97 content::DevToolsManager::GetInstance()->AddAgentStateCallback(
98 devtools_callback_);
100 host_->view()->browser()->tab_strip_model()->AddObserver(this);
103 ExtensionPopup::~ExtensionPopup() {
104 content::DevToolsManager::GetInstance()->RemoveAgentStateCallback(
105 devtools_callback_);
107 host_->view()->browser()->tab_strip_model()->RemoveObserver(this);
110 void ExtensionPopup::Observe(int type,
111 const content::NotificationSource& source,
112 const content::NotificationDetails& details) {
113 switch (type) {
114 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME:
115 DCHECK(content::Source<WebContents>(host()->host_contents()) == source);
116 // Show when the content finishes loading and its width is computed.
117 ShowBubble();
118 break;
119 case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE:
120 // If we aren't the host of the popup, then disregard the notification.
121 if (content::Details<extensions::ExtensionHost>(host()) == details)
122 GetWidget()->Close();
123 break;
124 default:
125 NOTREACHED() << L"Received unexpected notification";
129 void ExtensionPopup::OnDevToolsStateChanged(
130 content::DevToolsAgentHost* agent_host, bool attached) {
131 // First check that the devtools are being opened on this popup.
132 if (host()->render_view_host() != agent_host->GetRenderViewHost())
133 return;
135 if (attached) {
136 // Set inspect_with_devtools_ so the popup will be kept open while
137 // the devtools are open.
138 inspect_with_devtools_ = true;
139 } else {
140 // Widget::Close posts a task, which should give the devtools window a
141 // chance to finish detaching from the inspected RenderViewHost.
142 GetWidget()->Close();
146 void ExtensionPopup::OnExtensionSizeChanged(ExtensionViewViews* view) {
147 SizeToContents();
150 gfx::Size ExtensionPopup::GetPreferredSize() {
151 // Constrain the size to popup min/max.
152 gfx::Size sz = views::View::GetPreferredSize();
153 sz.set_width(std::max(kMinWidth, std::min(kMaxWidth, sz.width())));
154 sz.set_height(std::max(kMinHeight, std::min(kMaxHeight, sz.height())));
155 return sz;
158 void ExtensionPopup::OnWidgetDestroying(views::Widget* widget) {
159 BubbleDelegateView::OnWidgetDestroying(widget);
160 #if defined(USE_AURA)
161 aura::Window* bubble_window = GetWidget()->GetNativeWindow();
162 aura::client::ActivationClient* activation_client =
163 aura::client::GetActivationClient(bubble_window->GetRootWindow());
164 activation_client->RemoveObserver(this);
165 #endif
168 void ExtensionPopup::OnWidgetActivationChanged(views::Widget* widget,
169 bool active) {
170 // Dismiss only if the window being activated is not owned by this popup's
171 // window. In particular, don't dismiss when we lose activation to a child
172 // dialog box. Possibly relevant: http://crbug.com/106723 and
173 // http://crbug.com/179786
174 views::Widget* this_widget = GetWidget();
176 // TODO(msw): Resolve crashes and remove checks. See: http://crbug.com/327776
177 CHECK(!close_on_deactivate());
178 CHECK(this_widget);
179 CHECK(widget);
181 gfx::NativeView activated_view = widget->GetNativeView();
182 gfx::NativeView this_view = this_widget->GetNativeView();
183 if (active && !inspect_with_devtools_ && activated_view != this_view &&
184 !IsOwnerOf(activated_view, this_view))
185 this_widget->Close();
188 #if defined(USE_AURA)
189 void ExtensionPopup::OnWindowActivated(aura::Window* gained_active,
190 aura::Window* lost_active) {
191 // DesktopNativeWidgetAura does not trigger the expected browser widget
192 // [de]activation events when activating widgets in its own root window.
193 // This additional check handles those cases. See: http://crbug.com/320889
194 aura::Window* this_window = GetWidget()->GetNativeWindow();
195 aura::Window* anchor_window = anchor_widget()->GetNativeWindow();
196 chrome::HostDesktopType host_desktop_type =
197 chrome::GetHostDesktopTypeForNativeWindow(this_window);
198 if (!inspect_with_devtools_ && anchor_window == gained_active &&
199 host_desktop_type != chrome::HOST_DESKTOP_TYPE_ASH &&
200 this_window->GetRootWindow() == anchor_window->GetRootWindow() &&
201 views::corewm::GetTransientParent(gained_active) != this_window)
202 GetWidget()->Close();
204 #endif
206 void ExtensionPopup::ActiveTabChanged(content::WebContents* old_contents,
207 content::WebContents* new_contents,
208 int index,
209 int reason) {
210 GetWidget()->Close();
213 // static
214 ExtensionPopup* ExtensionPopup::ShowPopup(const GURL& url,
215 Browser* browser,
216 views::View* anchor_view,
217 views::BubbleBorder::Arrow arrow,
218 ShowAction show_action) {
219 extensions::ExtensionViewHost* host =
220 extensions::ExtensionViewHostFactory::CreatePopupHost(url, browser);
221 ExtensionPopup* popup = new ExtensionPopup(host, anchor_view, arrow,
222 show_action);
223 views::BubbleDelegateView::CreateBubble(popup);
225 #if defined(USE_AURA)
226 gfx::NativeView native_view = popup->GetWidget()->GetNativeView();
227 views::corewm::SetWindowVisibilityAnimationType(
228 native_view,
229 views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL);
230 views::corewm::SetWindowVisibilityAnimationVerticalPosition(
231 native_view,
232 -3.0f);
233 #endif
235 // If the host had somehow finished loading, then we'd miss the notification
236 // and not show. This seems to happen in single-process mode.
237 if (host->did_stop_loading())
238 popup->ShowBubble();
240 #if defined(USE_AURA)
241 aura::Window* bubble_window = popup->GetWidget()->GetNativeWindow();
242 aura::client::ActivationClient* activation_client =
243 aura::client::GetActivationClient(bubble_window->GetRootWindow());
244 activation_client->AddObserver(popup);
245 #endif
247 return popup;
250 void ExtensionPopup::ShowBubble() {
251 GetWidget()->Show();
253 // Focus on the host contents when the bubble is first shown.
254 host()->host_contents()->GetView()->Focus();
256 if (inspect_with_devtools_) {
257 DevToolsWindow::OpenDevToolsWindow(host()->render_view_host(),
258 DevToolsToggleAction::ShowConsole());