extension_popup.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
15254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieu// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2b6d54e56a5c65c2728080578b84374c3c594daecSteve Naroff// Use of this source code is governed by a BSD-style license that can be 3b6d54e56a5c65c2728080578b84374c3c594daecSteve Naroff// found in the LICENSE file. 4bed28ac1d1463adca3ecf24fca5c30646fa9dbb2Sylvestre Ledru 5aa58f00ebba5f14955001736b7aea20bb5bd91e6Steve Naroff#include "chrome/browser/ui/views/extensions/extension_popup.h" 6aa58f00ebba5f14955001736b7aea20bb5bd91e6Steve Naroff 7890d93eec45f2ba720dce98e34a1a904697ae842Steve Naroff#include "base/bind.h" 8aaffbf7c790a324ed114184db771aae2d2e9151cSteve Naroff#include "chrome/browser/chrome_notification_types.h" 9aaffbf7c790a324ed114184db771aae2d2e9151cSteve Naroff#include "chrome/browser/devtools/devtools_window.h" 10b6d54e56a5c65c2728080578b84374c3c594daecSteve Naroff#include "chrome/browser/extensions/extension_view_host.h" 11b6d54e56a5c65c2728080578b84374c3c594daecSteve Naroff#include "chrome/browser/extensions/extension_view_host_factory.h" 12b6d54e56a5c65c2728080578b84374c3c594daecSteve Naroff#include "chrome/browser/ui/browser.h" 13b6d54e56a5c65c2728080578b84374c3c594daecSteve Naroff#include "chrome/browser/ui/tabs/tab_strip_model.h" 14b6d54e56a5c65c2728080578b84374c3c594daecSteve Naroff#include "content/public/browser/devtools_agent_host.h" 15b6d54e56a5c65c2728080578b84374c3c594daecSteve Naroff#include "content/public/browser/devtools_manager.h" 16d4eea8362605807327735727a9098abe1eb23b19Douglas Gregor#include "content/public/browser/notification_details.h" 17d4eea8362605807327735727a9098abe1eb23b19Douglas Gregor#include "content/public/browser/notification_source.h" 18b6d54e56a5c65c2728080578b84374c3c594daecSteve Naroff#include "content/public/browser/render_view_host.h" 19b6d54e56a5c65c2728080578b84374c3c594daecSteve Naroff#include "content/public/browser/web_contents.h" 20d4eea8362605807327735727a9098abe1eb23b19Douglas Gregor#include "ui/aura/window.h" 21f76f5ed0505bc4b9c3c7d6003335b34cdf9afe47Eli Friedman#include "ui/gfx/insets.h" 22f76f5ed0505bc4b9c3c7d6003335b34cdf9afe47Eli Friedman#include "ui/views/layout/fill_layout.h" 23f76f5ed0505bc4b9c3c7d6003335b34cdf9afe47Eli Friedman#include "ui/views/widget/widget.h" 24f76f5ed0505bc4b9c3c7d6003335b34cdf9afe47Eli Friedman#include "ui/wm/core/window_animations.h" 254c721d381fb279899337d120edd4a24d405e56b2Eli Friedman#include "ui/wm/core/window_util.h" 264c721d381fb279899337d120edd4a24d405e56b2Eli Friedman#include "ui/wm/public/activation_client.h" 274c721d381fb279899337d120edd4a24d405e56b2Eli Friedman 284c721d381fb279899337d120edd4a24d405e56b2Eli Friedmannamespace { 294c721d381fb279899337d120edd4a24d405e56b2Eli Friedman 30bab96968886f4b77083f4e26a28986ddb1e42d67Eli FriedmanExtensionViewViews* GetExtensionView(extensions::ExtensionViewHost* host) { 31bab96968886f4b77083f4e26a28986ddb1e42d67Eli Friedman return static_cast<ExtensionViewViews*>(host->view()); 32bab96968886f4b77083f4e26a28986ddb1e42d67Eli Friedman} 33bab96968886f4b77083f4e26a28986ddb1e42d67Eli Friedman 344b3f9b367c16e494c181d6f03d53497ae1275fbeEli Friedman} // namespace 354b3f9b367c16e494c181d6f03d53497ae1275fbeEli Friedman 364b3f9b367c16e494c181d6f03d53497ae1275fbeEli Friedman// The minimum/maximum dimensions of the popup. 37b13c87f0c9705d91d5a3e134be9934c9ad531071John McCall// The minimum is just a little larger than the size of the button itself. 38b13c87f0c9705d91d5a3e134be9934c9ad531071John McCall// The maximum is an arbitrary number that should be smaller than most screens. 395254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieuconst int ExtensionPopup::kMinWidth = 25; 405254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieuconst int ExtensionPopup::kMinHeight = 25; 415254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieuconst int ExtensionPopup::kMaxWidth = 800; 425254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieuconst int ExtensionPopup::kMaxHeight = 600; 435254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieu 445254161b269829b74e7a9379b1bdfa27de72d7ccRichard TrieuExtensionPopup::ExtensionPopup(extensions::ExtensionViewHost* host, 45b13c87f0c9705d91d5a3e134be9934c9ad531071John McCall views::View* anchor_view, 46b13c87f0c9705d91d5a3e134be9934c9ad531071John McCall views::BubbleBorder::Arrow arrow, 47b13c87f0c9705d91d5a3e134be9934c9ad531071John McCall ShowAction show_action) 48b13c87f0c9705d91d5a3e134be9934c9ad531071John McCall : BubbleDelegateView(anchor_view, arrow), 49b13c87f0c9705d91d5a3e134be9934c9ad531071John McCall host_(host), 50b13c87f0c9705d91d5a3e134be9934c9ad531071John McCall devtools_callback_(base::Bind( 51b13c87f0c9705d91d5a3e134be9934c9ad531071John McCall &ExtensionPopup::OnDevToolsStateChanged, base::Unretained(this))), 525254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieu widget_initialized_(false) { 53b13c87f0c9705d91d5a3e134be9934c9ad531071John McCall inspect_with_devtools_ = show_action == SHOW_AND_INSPECT; 54b13c87f0c9705d91d5a3e134be9934c9ad531071John McCall // Adjust the margin so that contents fit better. 555254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieu const int margin = views::BubbleBorder::GetCornerRadius() / 2; 565254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieu set_margins(gfx::Insets(margin, margin, margin, margin)); 57323ed74658bc8375278eabf074b4777458376540John McCall SetLayoutManager(new views::FillLayout()); 585254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieu AddChildView(GetExtensionView(host)); 595254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieu GetExtensionView(host)->set_container(this); 605254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieu // ExtensionPopup closes itself on very specific de-activation conditions. 615254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieu set_close_on_deactivate(false); 625254161b269829b74e7a9379b1bdfa27de72d7ccRichard Trieu 63ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman // Wait to show the popup until the contained host finishes loading. 64ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman registrar_.Add(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, 65ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman content::Source<content::WebContents>(host->host_contents())); 66ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman 67ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman // Listen for the containing view calling window.close(); 68ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman registrar_.Add( 69ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman this, 70ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE, 71ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman content::Source<content::BrowserContext>(host->browser_context())); 72ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman content::DevToolsManager::GetInstance()->AddAgentStateCallback( 73ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman devtools_callback_); 74ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman 75ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman GetExtensionView(host)->GetBrowser()->tab_strip_model()->AddObserver(this); 76ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman} 77ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman 78ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli FriedmanExtensionPopup::~ExtensionPopup() { 79ae916a14cf727b4ce3ac316f4fd780d1c83c5bafEli Friedman content::DevToolsManager::GetInstance()->RemoveAgentStateCallback( 80b6d54e56a5c65c2728080578b84374c3c594daecSteve Naroff devtools_callback_); 81b6d54e56a5c65c2728080578b84374c3c594daecSteve Naroff 82e701c0a953d05c3403a74fdb449a8f4a1e4e6594Steve Naroff GetExtensionView( 83e701c0a953d05c3403a74fdb449a8f4a1e4e6594Steve Naroff host_.get())->GetBrowser()->tab_strip_model()->RemoveObserver(this); 84e701c0a953d05c3403a74fdb449a8f4a1e4e6594Steve Naroff} 85e701c0a953d05c3403a74fdb449a8f4a1e4e6594Steve Naroff 869158804749356f88be8c5a3bade75b761846273cSteve Naroffvoid ExtensionPopup::Observe(int type, 879158804749356f88be8c5a3bade75b761846273cSteve Naroff const content::NotificationSource& source, 889158804749356f88be8c5a3bade75b761846273cSteve Naroff const content::NotificationDetails& details) { 899158804749356f88be8c5a3bade75b761846273cSteve Naroff switch (type) { 909158804749356f88be8c5a3bade75b761846273cSteve Naroff case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: 919158804749356f88be8c5a3bade75b761846273cSteve Naroff DCHECK_EQ(host()->host_contents(), 929158804749356f88be8c5a3bade75b761846273cSteve Naroff content::Source<content::WebContents>(source).ptr()); 93d4eea8362605807327735727a9098abe1eb23b19Douglas Gregor // Show when the content finishes loading and its width is computed. 949158804749356f88be8c5a3bade75b761846273cSteve Naroff ShowBubble(); 95323ed74658bc8375278eabf074b4777458376540John McCall break; 96323ed74658bc8375278eabf074b4777458376540John McCall case extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: 97323ed74658bc8375278eabf074b4777458376540John McCall // If we aren't the host of the popup, then disregard the notification. 98323ed74658bc8375278eabf074b4777458376540John McCall if (content::Details<extensions::ExtensionHost>(host()) == details) 99323ed74658bc8375278eabf074b4777458376540John McCall GetWidget()->Close(); 10082214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruth break; 10182214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruth default: 10282214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruth NOTREACHED() << L"Received unexpected notification"; 10382214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruth } 10482214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruth} 10582214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruth 10682214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruthvoid ExtensionPopup::OnDevToolsStateChanged( 10782214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruth content::DevToolsAgentHost* agent_host, 10882214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruth bool attached) { 10982214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruth // First check that the devtools are being opened on this popup. 11082214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruth if (host()->render_view_host() != agent_host->GetRenderViewHost()) 11182214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruth return; 11282214a80c0163e01e4d8dec1426023c89277dbb4Chandler Carruth 113 if (attached) { 114 // Set inspect_with_devtools_ so the popup will be kept open while 115 // the devtools are open. 116 inspect_with_devtools_ = true; 117 } else { 118 // Widget::Close posts a task, which should give the devtools window a 119 // chance to finish detaching from the inspected RenderViewHost. 120 GetWidget()->Close(); 121 } 122} 123 124void ExtensionPopup::OnExtensionSizeChanged(ExtensionViewViews* view) { 125 SizeToContents(); 126} 127 128gfx::Size ExtensionPopup::GetPreferredSize() const { 129 // Constrain the size to popup min/max. 130 gfx::Size sz = views::View::GetPreferredSize(); 131 sz.set_width(std::max(kMinWidth, std::min(kMaxWidth, sz.width()))); 132 sz.set_height(std::max(kMinHeight, std::min(kMaxHeight, sz.height()))); 133 return sz; 134} 135 136void ExtensionPopup::ViewHierarchyChanged( 137 const ViewHierarchyChangedDetails& details) { 138 // TODO(msw): Find any remaining crashes related to http://crbug.com/327776 139 // No view hierarchy changes are expected if the widget no longer exists. 140 widget_initialized_ |= details.child == this && details.is_add && GetWidget(); 141 CHECK(GetWidget() || !widget_initialized_); 142} 143 144void ExtensionPopup::OnWidgetDestroying(views::Widget* widget) { 145 BubbleDelegateView::OnWidgetDestroying(widget); 146 aura::Window* bubble_window = GetWidget()->GetNativeWindow(); 147 aura::client::ActivationClient* activation_client = 148 aura::client::GetActivationClient(bubble_window->GetRootWindow()); 149 // If the popup was being inspected with devtools and the browser window was 150 // closed, then the root window and activation client are already destroyed. 151 if (activation_client) 152 activation_client->RemoveObserver(this); 153} 154 155void ExtensionPopup::OnWidgetActivationChanged(views::Widget* widget, 156 bool active) { 157 // TODO(msw): Find any remaining crashes related to http://crbug.com/327776 158 // No calls are expected if the widget isn't initialized or no longer exists. 159 CHECK(widget_initialized_); 160 CHECK(GetWidget()); 161 162 // Close on anchor window activation (ie. user clicked the browser window). 163 if (!inspect_with_devtools_ && widget && active && 164 widget->GetNativeWindow() == anchor_widget()->GetNativeWindow()) 165 GetWidget()->Close(); 166} 167 168void ExtensionPopup::OnWindowActivated(aura::Window* gained_active, 169 aura::Window* lost_active) { 170 // TODO(msw): Find any remaining crashes related to http://crbug.com/327776 171 // No calls are expected if the widget isn't initialized or no longer exists. 172 CHECK(widget_initialized_); 173 CHECK(GetWidget()); 174 175 // Close on anchor window activation (ie. user clicked the browser window). 176 // DesktopNativeWidgetAura does not trigger the expected browser widget 177 // [de]activation events when activating widgets in its own root window. 178 // This additional check handles those cases. See: http://crbug.com/320889 179 if (!inspect_with_devtools_ && 180 gained_active == anchor_widget()->GetNativeWindow()) 181 GetWidget()->Close(); 182} 183 184void ExtensionPopup::ActiveTabChanged(content::WebContents* old_contents, 185 content::WebContents* new_contents, 186 int index, 187 int reason) { 188 GetWidget()->Close(); 189} 190 191// static 192ExtensionPopup* ExtensionPopup::ShowPopup(const GURL& url, 193 Browser* browser, 194 views::View* anchor_view, 195 views::BubbleBorder::Arrow arrow, 196 ShowAction show_action) { 197 extensions::ExtensionViewHost* host = 198 extensions::ExtensionViewHostFactory::CreatePopupHost(url, browser); 199 ExtensionPopup* popup = new ExtensionPopup(host, anchor_view, arrow, 200 show_action); 201 views::BubbleDelegateView::CreateBubble(popup); 202 203 gfx::NativeView native_view = popup->GetWidget()->GetNativeView(); 204 wm::SetWindowVisibilityAnimationType( 205 native_view, wm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL); 206 wm::SetWindowVisibilityAnimationVerticalPosition(native_view, -3.0f); 207 208 // If the host had somehow finished loading, then we'd miss the notification 209 // and not show. This seems to happen in single-process mode. 210 if (host->did_stop_loading()) 211 popup->ShowBubble(); 212 213 aura::Window* bubble_window = popup->GetWidget()->GetNativeWindow(); 214 aura::client::ActivationClient* activation_client = 215 aura::client::GetActivationClient(bubble_window->GetRootWindow()); 216 activation_client->AddObserver(popup); 217 218 return popup; 219} 220 221void ExtensionPopup::ShowBubble() { 222 GetWidget()->Show(); 223 224 // Focus on the host contents when the bubble is first shown. 225 host()->host_contents()->Focus(); 226 227 if (inspect_with_devtools_) { 228 DevToolsWindow::OpenDevToolsWindow(host()->render_view_host(), 229 DevToolsToggleAction::ShowConsole()); 230 } 231} 232