| OLD | NEW |
| (Empty) |
| 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/gtk/extensions/extension_popup_gtk.h" | |
| 6 | |
| 7 #include <gtk/gtk.h> | |
| 8 | |
| 9 #include <algorithm> | |
| 10 | |
| 11 #include "base/bind.h" | |
| 12 #include "base/bind_helpers.h" | |
| 13 #include "base/callback.h" | |
| 14 #include "base/i18n/rtl.h" | |
| 15 #include "base/message_loop/message_loop.h" | |
| 16 #include "chrome/browser/chrome_notification_types.h" | |
| 17 #include "chrome/browser/devtools/devtools_window.h" | |
| 18 #include "chrome/browser/extensions/extension_view_host.h" | |
| 19 #include "chrome/browser/extensions/extension_view_host_factory.h" | |
| 20 #include "chrome/browser/profiles/profile.h" | |
| 21 #include "chrome/browser/ui/browser.h" | |
| 22 #include "chrome/browser/ui/browser_window.h" | |
| 23 #include "chrome/browser/ui/gtk/gtk_theme_service.h" | |
| 24 #include "content/public/browser/devtools_agent_host.h" | |
| 25 #include "content/public/browser/devtools_manager.h" | |
| 26 #include "content/public/browser/notification_details.h" | |
| 27 #include "content/public/browser/notification_source.h" | |
| 28 #include "content/public/browser/render_view_host.h" | |
| 29 #include "content/public/browser/render_widget_host_view.h" | |
| 30 #include "url/gurl.h" | |
| 31 | |
| 32 using content::BrowserContext; | |
| 33 using content::RenderViewHost; | |
| 34 | |
| 35 ExtensionPopupGtk* ExtensionPopupGtk::current_extension_popup_ = NULL; | |
| 36 | |
| 37 // The minimum/maximum dimensions of the extension popup. | |
| 38 // The minimum is just a little larger than the size of a browser action button. | |
| 39 // The maximum is an arbitrary number that should be smaller than most screens. | |
| 40 const int ExtensionPopupGtk::kMinWidth = 25; | |
| 41 const int ExtensionPopupGtk::kMinHeight = 25; | |
| 42 const int ExtensionPopupGtk::kMaxWidth = 800; | |
| 43 const int ExtensionPopupGtk::kMaxHeight = 600; | |
| 44 | |
| 45 ExtensionPopupGtk::ExtensionPopupGtk(Browser* browser, | |
| 46 extensions::ExtensionViewHost* host, | |
| 47 GtkWidget* anchor, | |
| 48 ShowAction show_action) | |
| 49 : browser_(browser), | |
| 50 bubble_(NULL), | |
| 51 host_(host), | |
| 52 anchor_(anchor), | |
| 53 devtools_callback_(base::Bind( | |
| 54 &ExtensionPopupGtk::OnDevToolsStateChanged, base::Unretained(this))), | |
| 55 weak_factory_(this) { | |
| 56 host_->view()->SetContainer(this); | |
| 57 being_inspected_ = show_action == SHOW_AND_INSPECT; | |
| 58 | |
| 59 // If the host had somehow finished loading, then we'd miss the notification | |
| 60 // and not show. This seems to happen in single-process mode. | |
| 61 if (host->did_stop_loading()) { | |
| 62 ShowPopup(); | |
| 63 } else { | |
| 64 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, | |
| 65 content::Source<BrowserContext>(host->browser_context())); | |
| 66 } | |
| 67 | |
| 68 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE, | |
| 69 content::Source<BrowserContext>(host->browser_context())); | |
| 70 content::DevToolsManager::GetInstance()->AddAgentStateCallback( | |
| 71 devtools_callback_); | |
| 72 } | |
| 73 | |
| 74 ExtensionPopupGtk::~ExtensionPopupGtk() { | |
| 75 content::DevToolsManager::GetInstance()->RemoveAgentStateCallback( | |
| 76 devtools_callback_); | |
| 77 } | |
| 78 | |
| 79 // static | |
| 80 void ExtensionPopupGtk::Show(const GURL& url, Browser* browser, | |
| 81 GtkWidget* anchor, ShowAction show_action) { | |
| 82 extensions::ExtensionViewHost* host = | |
| 83 extensions::ExtensionViewHostFactory::CreatePopupHost(url, browser); | |
| 84 // This object will delete itself when the bubble is closed. | |
| 85 new ExtensionPopupGtk(browser, host, anchor, show_action); | |
| 86 } | |
| 87 | |
| 88 void ExtensionPopupGtk::Observe(int type, | |
| 89 const content::NotificationSource& source, | |
| 90 const content::NotificationDetails& details) { | |
| 91 switch (type) { | |
| 92 case chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING: | |
| 93 if (content::Details<extensions::ExtensionHost>(host_.get()) == details) | |
| 94 ShowPopup(); | |
| 95 break; | |
| 96 case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: | |
| 97 if (content::Details<extensions::ExtensionHost>(host_.get()) == details) | |
| 98 DestroyPopup(); | |
| 99 break; | |
| 100 default: | |
| 101 NOTREACHED() << "Received unexpected notification"; | |
| 102 } | |
| 103 } | |
| 104 | |
| 105 void ExtensionPopupGtk::OnDevToolsStateChanged( | |
| 106 content::DevToolsAgentHost* agent_host, bool attached) { | |
| 107 // Make sure it's the devtools window that is inspecting our popup. | |
| 108 if (host_->render_view_host() != agent_host->GetRenderViewHost()) | |
| 109 return; | |
| 110 if (attached) { | |
| 111 // Make sure that the popup won't go away when the inspector is activated. | |
| 112 if (bubble_) | |
| 113 bubble_->StopGrabbingInput(); | |
| 114 | |
| 115 being_inspected_ = true; | |
| 116 } else { | |
| 117 // If the devtools window is closing, we post a task to ourselves to | |
| 118 // close the popup. This gives the devtools window a chance to finish | |
| 119 // detaching from the inspected RenderViewHost. | |
| 120 base::MessageLoop::current()->PostTask( | |
| 121 FROM_HERE, | |
| 122 base::Bind(&ExtensionPopupGtk::DestroyPopupWithoutResult, | |
| 123 weak_factory_.GetWeakPtr())); | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 void ExtensionPopupGtk::BubbleClosing(BubbleGtk* bubble, | |
| 128 bool closed_by_escape) { | |
| 129 current_extension_popup_ = NULL; | |
| 130 delete this; | |
| 131 } | |
| 132 | |
| 133 void ExtensionPopupGtk::OnExtensionSizeChanged( | |
| 134 ExtensionViewGtk* view, | |
| 135 const gfx::Size& new_size) { | |
| 136 int width = std::max(kMinWidth, std::min(kMaxWidth, new_size.width())); | |
| 137 int height = std::max(kMinHeight, std::min(kMaxHeight, new_size.height())); | |
| 138 | |
| 139 view->render_view_host()->GetView()->SetSize(gfx::Size(width, height)); | |
| 140 gtk_widget_set_size_request(view->native_view(), width, height); | |
| 141 } | |
| 142 | |
| 143 bool ExtensionPopupGtk::DestroyPopup() { | |
| 144 if (!bubble_) { | |
| 145 NOTREACHED(); | |
| 146 return false; | |
| 147 } | |
| 148 | |
| 149 bubble_->Close(); | |
| 150 return true; | |
| 151 } | |
| 152 | |
| 153 void ExtensionPopupGtk::ShowPopup() { | |
| 154 if (bubble_) { | |
| 155 NOTREACHED(); | |
| 156 return; | |
| 157 } | |
| 158 | |
| 159 if (being_inspected_) | |
| 160 DevToolsWindow::OpenDevToolsWindow(host_->render_view_host()); | |
| 161 | |
| 162 // Only one instance should be showing at a time. Get rid of the old one, if | |
| 163 // any. Typically, |current_extension_popup_| will be NULL, but it can be | |
| 164 // non-NULL if a browser action button is clicked while another extension | |
| 165 // popup's extension host is still loading. | |
| 166 if (current_extension_popup_) | |
| 167 current_extension_popup_->DestroyPopup(); | |
| 168 current_extension_popup_ = this; | |
| 169 | |
| 170 GtkWidget* border_box = gtk_alignment_new(0, 0, 1.0, 1.0); | |
| 171 // This border is necessary so the bubble's corners do not get cut off by the | |
| 172 // render view. | |
| 173 gtk_container_set_border_width(GTK_CONTAINER(border_box), 2); | |
| 174 gtk_container_add(GTK_CONTAINER(border_box), host_->view()->native_view()); | |
| 175 | |
| 176 // We'll be in the upper-right corner of the window for LTR languages, so we | |
| 177 // want to put the arrow at the upper-right corner of the bubble to match the | |
| 178 // page and app menus. | |
| 179 bubble_ = BubbleGtk::Show(anchor_, | |
| 180 NULL, | |
| 181 border_box, | |
| 182 BubbleGtk::ANCHOR_TOP_RIGHT, | |
| 183 being_inspected_ ? 0 : | |
| 184 BubbleGtk::POPUP_WINDOW | BubbleGtk::GRAB_INPUT, | |
| 185 GtkThemeService::GetFrom(browser_->profile()), | |
| 186 this); | |
| 187 } | |
| 188 | |
| 189 void ExtensionPopupGtk::DestroyPopupWithoutResult() { | |
| 190 DestroyPopup(); | |
| 191 } | |
| 192 | |
| 193 gfx::Rect ExtensionPopupGtk::GetViewBounds() { | |
| 194 GtkAllocation allocation; | |
| 195 gtk_widget_get_allocation(host_->view()->native_view(), &allocation); | |
| 196 return gfx::Rect(allocation); | |
| 197 } | |
| OLD | NEW |