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
572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/views/extensions/extension_popup.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include <vector>
821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/debugger/devtools_manager.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/debugger/devtools_toggle_action.h"
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_host.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_process_manager.h"
1321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h"
144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/browser/ui/browser.h"
1521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/browser_window.h"
1621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/views/frame/browser_view.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension.h"
18dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/render_view_host.h"
19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/browser/renderer_host/render_widget_host_view.h"
20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_details.h"
21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_source.h"
22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_type.h"
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/root_view.h"
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/window/window.h"
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_LINUX)
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/widget_gtk.h"
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_CHROMEOS)
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chromeos/wm_ipc.h"
3221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "third_party/cros/chromeos_wm_ipc_enums.h"
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
3521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenusing std::vector;
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing views::Widget;
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
3872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// The minimum/maximum dimensions of the popup.
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The minimum is just a little larger than the size of the button itself.
4072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// The maximum is an arbitrary number that should be smaller than most screens.
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int ExtensionPopup::kMinWidth = 25;
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int ExtensionPopup::kMinHeight = 25;
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int ExtensionPopup::kMaxWidth = 800;
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int ExtensionPopup::kMaxHeight = 600;
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochExtensionPopup::ExtensionPopup(ExtensionHost* host,
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               views::Widget* frame,
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               const gfx::Rect& relative_to,
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               BubbleBorder::ArrowLocation arrow_location,
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               bool inspect_with_devtools,
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               Observer* observer)
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : BrowserBubble(host->view(),
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    frame,
54ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                    relative_to,
55ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                    arrow_location),
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      relative_to_(relative_to),
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      extension_host_(host),
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      inspect_with_devtools_(inspect_with_devtools),
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      close_on_lost_focus_(true),
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      closing_(false),
6172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      observer_(observer) {
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AddRef();  // Balanced in Close();
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  set_delegate(this);
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  host->view()->SetContainer(this);
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We wait to show the popup until the contained host finishes loading.
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  registrar_.Add(this,
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 Source<Profile>(host->profile()));
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Listen for the containing view calling window.close();
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE,
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 Source<Profile>(host->profile()));
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochExtensionPopup::~ExtensionPopup() {
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionPopup::Show(bool activate) {
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (visible())
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN)
8472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  frame_->GetWindow()->DisableInactiveRendering();
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ResizeToView();
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BrowserBubble::Show(activate);
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionPopup::BubbleBrowserWindowMoved(BrowserBubble* bubble) {
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ResizeToView();
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionPopup::BubbleBrowserWindowClosing(BrowserBubble* bubble) {
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!closing_)
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Close();
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionPopup::BubbleGotFocus(BrowserBubble* bubble) {
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Forward the focus to the renderer.
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  host()->render_view_host()->view()->Focus();
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionPopup::BubbleLostFocus(BrowserBubble* bubble,
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bool lost_focus_to_child) {
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (closing_ ||                // We are already closing.
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      inspect_with_devtools_ ||  // The popup is being inspected.
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      !close_on_lost_focus_ ||   // Our client is handling focus listening.
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      lost_focus_to_child)       // A child of this view got focus.
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // When we do close on BubbleLostFocus, we do it in the next event loop
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // because a subsequent event in this loop may also want to close this popup
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // and if so, we want to allow that. Example: Clicking the same browser
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // action button that opened the popup. If we closed immediately, the
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // browser action container would fail to discover that the same button
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // was pressed.
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(this,
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      &ExtensionPopup::Close));
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionPopup::Observe(NotificationType type,
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                             const NotificationSource& source,
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                             const NotificationDetails& details) {
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (type.value) {
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Once we receive did stop loading, the content will be complete and
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // the width will have been computed.  Now it's safe to show.
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (extension_host_.get() == Details<ExtensionHost>(details).ptr()) {
13272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        Show(true);
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (inspect_with_devtools_) {
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          // Listen for the the devtools window closing.
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          registrar_.Add(this, NotificationType::DEVTOOLS_WINDOW_CLOSING,
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              Source<Profile>(extension_host_->profile()));
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          DevToolsManager::GetInstance()->ToggleDevToolsWindow(
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              extension_host_->render_view_host(),
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE);
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE:
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // If we aren't the host of the popup, then disregard the notification.
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (Details<ExtensionHost>(host()) != details)
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return;
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Close();
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case NotificationType::DEVTOOLS_WINDOW_CLOSING:
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Make sure its the devtools window that inspecting our popup.
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (Details<RenderViewHost>(extension_host_->render_view_host()) !=
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          details)
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return;
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // If the devtools window is closing, we post a task to ourselves to
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // close the popup. This gives the devtools window a chance to finish
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // detaching from the inspected RenderViewHost.
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(this,
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          &ExtensionPopup::Close));
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default:
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED() << L"Received unexpected notification";
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionPopup::OnExtensionPreferredSizeChanged(ExtensionView* view) {
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Constrain the size to popup min/max.
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size sz = view->GetPreferredSize();
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  view->SetBounds(view->x(), view->y(),
17372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      std::max(kMinWidth, std::min(kMaxWidth, sz.width())),
17472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      std::max(kMinHeight, std::min(kMaxHeight, sz.height())));
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ResizeToView();
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochExtensionPopup* ExtensionPopup::Show(
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const GURL& url,
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Browser* browser,
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const gfx::Rect& relative_to,
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BubbleBorder::ArrowLocation arrow_location,
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bool inspect_with_devtools,
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Observer* observer) {
18772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ExtensionProcessManager* manager =
18872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      browser->profile()->GetExtensionProcessManager();
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(manager);
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!manager)
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return NULL;
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ExtensionHost* host = manager->CreatePopup(url, browser);
19472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  views::Widget* frame = BrowserView::GetBrowserViewForNativeWindow(
19572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      browser->window()->GetNativeHandle())->GetWidget();
19672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ExtensionPopup* popup = new ExtensionPopup(host, frame, relative_to,
19772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                             arrow_location,
19872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                             inspect_with_devtools, observer);
199201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If the host had somehow finished loading, then we'd miss the notification
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // and not show.  This seems to happen in single-process mode.
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (host->did_stop_loading())
20372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    popup->Show(true);
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return popup;
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionPopup::Close() {
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (closing_)
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  closing_ = true;
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DetachFromBrowser();
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (observer_)
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    observer_->ExtensionPopupIsClosing(this);
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Release();  // Balanced in ctor.
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
219