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