Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / extensions / extension_popup.cc
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.
4
5 #include "chrome/browser/ui/views/extensions/extension_popup.h"
6
7 #include "base/bind.h"
8 #include "chrome/browser/chrome_notification_types.h"
9 #include "chrome/browser/devtools/devtools_window.h"
10 #include "chrome/browser/extensions/extension_view_host.h"
11 #include "chrome/browser/extensions/extension_view_host_factory.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "content/public/browser/devtools_agent_host.h"
15 #include "content/public/browser/devtools_manager.h"
16 #include "content/public/browser/notification_details.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/render_view_host.h"
19 #include "content/public/browser/web_contents.h"
20 #include "ui/aura/window.h"
21 #include "ui/gfx/insets.h"
22 #include "ui/views/layout/fill_layout.h"
23 #include "ui/views/widget/widget.h"
24 #include "ui/wm/core/window_animations.h"
25 #include "ui/wm/core/window_util.h"
26 #include "ui/wm/public/activation_client.h"
27
28 namespace {
29
30 ExtensionViewViews* GetExtensionView(extensions::ExtensionViewHost* host) {
31   return static_cast<ExtensionViewViews*>(host->view());
32 }
33
34 }  // namespace
35
36 // The minimum/maximum dimensions of the popup.
37 // The minimum is just a little larger than the size of the button itself.
38 // The maximum is an arbitrary number that should be smaller than most screens.
39 const int ExtensionPopup::kMinWidth = 25;
40 const int ExtensionPopup::kMinHeight = 25;
41 const int ExtensionPopup::kMaxWidth = 800;
42 const int ExtensionPopup::kMaxHeight = 600;
43
44 ExtensionPopup::ExtensionPopup(extensions::ExtensionViewHost* host,
45                                views::View* anchor_view,
46                                views::BubbleBorder::Arrow arrow,
47                                ShowAction show_action)
48     : BubbleDelegateView(anchor_view, arrow),
49       host_(host),
50       devtools_callback_(base::Bind(
51           &ExtensionPopup::OnDevToolsStateChanged, base::Unretained(this))),
52       widget_initialized_(false) {
53   inspect_with_devtools_ = show_action == SHOW_AND_INSPECT;
54   // Adjust the margin so that contents fit better.
55   const int margin = views::BubbleBorder::GetCornerRadius() / 2;
56   set_margins(gfx::Insets(margin, margin, margin, margin));
57   SetLayoutManager(new views::FillLayout());
58   AddChildView(GetExtensionView(host));
59   GetExtensionView(host)->set_container(this);
60   // ExtensionPopup closes itself on very specific de-activation conditions.
61   set_close_on_deactivate(false);
62
63   // Wait to show the popup until the contained host finishes loading.
64   registrar_.Add(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
65                  content::Source<content::WebContents>(host->host_contents()));
66
67   // Listen for the containing view calling window.close();
68   registrar_.Add(
69       this,
70       extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
71       content::Source<content::BrowserContext>(host->browser_context()));
72   content::DevToolsManager::GetInstance()->AddAgentStateCallback(
73       devtools_callback_);
74
75   GetExtensionView(host)->GetBrowser()->tab_strip_model()->AddObserver(this);
76 }
77
78 ExtensionPopup::~ExtensionPopup() {
79   content::DevToolsManager::GetInstance()->RemoveAgentStateCallback(
80       devtools_callback_);
81
82   GetExtensionView(
83       host_.get())->GetBrowser()->tab_strip_model()->RemoveObserver(this);
84 }
85
86 void ExtensionPopup::Observe(int type,
87                              const content::NotificationSource& source,
88                              const content::NotificationDetails& details) {
89   switch (type) {
90     case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME:
91       DCHECK_EQ(host()->host_contents(),
92                 content::Source<content::WebContents>(source).ptr());
93       // Show when the content finishes loading and its width is computed.
94       ShowBubble();
95       break;
96     case extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE:
97       // If we aren't the host of the popup, then disregard the notification.
98       if (content::Details<extensions::ExtensionHost>(host()) == details)
99         GetWidget()->Close();
100       break;
101     default:
102       NOTREACHED() << L"Received unexpected notification";
103   }
104 }
105
106 void ExtensionPopup::OnDevToolsStateChanged(
107     content::DevToolsAgentHost* agent_host,
108     bool attached) {
109   // First check that the devtools are being opened on this popup.
110   if (host()->host_contents() != agent_host->GetWebContents())
111     return;
112
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
124 void ExtensionPopup::OnExtensionSizeChanged(ExtensionViewViews* view) {
125   SizeToContents();
126 }
127
128 gfx::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
136 void 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
144 void 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
155 void 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
168 void 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
184 void ExtensionPopup::ActiveTabChanged(content::WebContents* old_contents,
185                                       content::WebContents* new_contents,
186                                       int index,
187                                       int reason) {
188   GetWidget()->Close();
189 }
190
191 // static
192 ExtensionPopup* 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
221 void 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()->host_contents(),
229                                        DevToolsToggleAction::ShowConsole());
230   }
231 }