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