• 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/gtk/extensions/extension_popup_gtk.h"
6 
7 #include <gtk/gtk.h>
8 
9 #include <algorithm>
10 
11 #include "base/i18n/rtl.h"
12 #include "base/message_loop.h"
13 #include "chrome/browser/debugger/devtools_manager.h"
14 #include "chrome/browser/extensions/extension_host.h"
15 #include "chrome/browser/extensions/extension_process_manager.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_window.h"
20 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
21 #include "content/browser/renderer_host/render_view_host.h"
22 #include "content/common/notification_details.h"
23 #include "content/common/notification_source.h"
24 #include "googleurl/src/gurl.h"
25 
26 ExtensionPopupGtk* ExtensionPopupGtk::current_extension_popup_ = NULL;
27 
28 // The minimum/maximum dimensions of the extension popup.
29 // The minimum is just a little larger than the size of a browser action button.
30 // The maximum is an arbitrary number that should be smaller than most screens.
31 const int ExtensionPopupGtk::kMinWidth = 25;
32 const int ExtensionPopupGtk::kMinHeight = 25;
33 const int ExtensionPopupGtk::kMaxWidth = 800;
34 const int ExtensionPopupGtk::kMaxHeight = 600;
35 
ExtensionPopupGtk(Browser * browser,ExtensionHost * host,GtkWidget * anchor,bool inspect)36 ExtensionPopupGtk::ExtensionPopupGtk(Browser* browser,
37                                      ExtensionHost* host,
38                                      GtkWidget* anchor,
39                                      bool inspect)
40     : browser_(browser),
41       bubble_(NULL),
42       host_(host),
43       anchor_(anchor),
44       being_inspected_(inspect),
45       method_factory_(this) {
46   host_->view()->SetContainer(this);
47 
48   // If the host had somehow finished loading, then we'd miss the notification
49   // and not show.  This seems to happen in single-process mode.
50   if (host->did_stop_loading()) {
51     ShowPopup();
52   } else {
53     registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
54                    Source<Profile>(host->profile()));
55   }
56 
57   registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE,
58                  Source<Profile>(host->profile()));
59 }
60 
~ExtensionPopupGtk()61 ExtensionPopupGtk::~ExtensionPopupGtk() {
62 }
63 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)64 void ExtensionPopupGtk::Observe(NotificationType type,
65                                 const NotificationSource& source,
66                                 const NotificationDetails& details) {
67   switch (type.value) {
68     case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
69       if (Details<ExtensionHost>(host_.get()) == details)
70         ShowPopup();
71       break;
72     case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE:
73       if (Details<ExtensionHost>(host_.get()) == details)
74         DestroyPopup();
75       break;
76     case NotificationType::DEVTOOLS_WINDOW_CLOSING:
77       // Make sure its the devtools window that inspecting our popup.
78       if (Details<RenderViewHost>(host_->render_view_host()) != details)
79         break;
80 
81       // If the devtools window is closing, we post a task to ourselves to
82       // close the popup. This gives the devtools window a chance to finish
83       // detaching from the inspected RenderViewHost.
84       MessageLoop::current()->PostTask(FROM_HERE,
85           method_factory_.NewRunnableMethod(&ExtensionPopupGtk::DestroyPopup));
86       break;
87     default:
88       NOTREACHED() << "Received unexpected notification";
89   }
90 }
91 
ShowPopup()92 void ExtensionPopupGtk::ShowPopup() {
93   if (bubble_) {
94     NOTREACHED();
95     return;
96   }
97 
98   if (being_inspected_) {
99     DevToolsManager::GetInstance()->OpenDevToolsWindow(
100         host_->render_view_host());
101     // Listen for the the devtools window closing.
102     registrar_.Add(this, NotificationType::DEVTOOLS_WINDOW_CLOSING,
103         Source<Profile>(host_->profile()));
104   }
105 
106   // Only one instance should be showing at a time. Get rid of the old one, if
107   // any. Typically, |current_extension_popup_| will be NULL, but it can be
108   // non-NULL if a browser action button is clicked while another extension
109   // popup's extension host is still loading.
110   if (current_extension_popup_)
111     current_extension_popup_->DestroyPopup();
112   current_extension_popup_ = this;
113 
114   // We'll be in the upper-right corner of the window for LTR languages, so we
115   // want to put the arrow at the upper-right corner of the bubble to match the
116   // page and app menus.
117   InfoBubbleGtk::ArrowLocationGtk arrow_location =
118       !base::i18n::IsRTL() ?
119       InfoBubbleGtk::ARROW_LOCATION_TOP_RIGHT :
120       InfoBubbleGtk::ARROW_LOCATION_TOP_LEFT;
121   bubble_ = InfoBubbleGtk::Show(anchor_,
122                                 NULL,
123                                 host_->view()->native_view(),
124                                 arrow_location,
125                                 false,  // match_system_theme
126                                 !being_inspected_,  // grab_input
127                                 GtkThemeService::GetFrom(browser_->profile()),
128                                 this);
129 }
130 
DestroyPopup()131 bool ExtensionPopupGtk::DestroyPopup() {
132   if (!bubble_) {
133     NOTREACHED();
134     return false;
135   }
136 
137   bubble_->Close();
138   return true;
139 }
140 
InfoBubbleClosing(InfoBubbleGtk * bubble,bool closed_by_escape)141 void ExtensionPopupGtk::InfoBubbleClosing(InfoBubbleGtk* bubble,
142                                           bool closed_by_escape) {
143   current_extension_popup_ = NULL;
144   delete this;
145 }
146 
OnExtensionPreferredSizeChanged(ExtensionViewGtk * view,const gfx::Size & new_size)147 void ExtensionPopupGtk::OnExtensionPreferredSizeChanged(
148     ExtensionViewGtk* view,
149     const gfx::Size& new_size) {
150   int width = std::max(kMinWidth, std::min(kMaxWidth, new_size.width()));
151   int height = std::max(kMinHeight, std::min(kMaxHeight, new_size.height()));
152 
153   view->render_view_host()->view()->SetSize(gfx::Size(width, height));
154   gtk_widget_set_size_request(view->native_view(), width, height);
155 }
156 
157 // static
Show(const GURL & url,Browser * browser,GtkWidget * anchor,bool inspect)158 void ExtensionPopupGtk::Show(const GURL& url, Browser* browser,
159     GtkWidget* anchor, bool inspect) {
160   ExtensionProcessManager* manager =
161       browser->profile()->GetExtensionProcessManager();
162   DCHECK(manager);
163   if (!manager)
164     return;
165 
166   ExtensionHost* host = manager->CreatePopup(url, browser);
167   // This object will delete itself when the info bubble is closed.
168   new ExtensionPopupGtk(browser, host, anchor, inspect);
169 }
170 
GetViewBounds()171 gfx::Rect ExtensionPopupGtk::GetViewBounds() {
172   return gfx::Rect(host_->view()->native_view()->allocation);
173 }
174