blob: 2dc1859a5e80ad71a86bb26746f39448af207304 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/extensions/extension_popup.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/extensions/extension_view_host.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/widget/widget.h"
// The minimum/maximum dimensions of the popup.
// The minimum is just a little larger than the size of the button itself.
// The maximum is an arbitrary number that should be smaller than most screens.
const int ExtensionPopup::kMinWidth = 25;
const int ExtensionPopup::kMinHeight = 25;
const int ExtensionPopup::kMaxWidth = 800;
const int ExtensionPopup::kMaxHeight = 600;
#if !defined(USE_AURA)
// static
ExtensionPopup* ExtensionPopup::Create(extensions::ExtensionViewHost* host,
views::View* anchor_view,
views::BubbleBorder::Arrow arrow,
ShowAction show_action) {
auto* popup = new ExtensionPopup(host, anchor_view, arrow, show_action);
views::BubbleDialogDelegateView::CreateBubble(popup);
return popup;
}
#endif
ExtensionPopup::ExtensionPopup(extensions::ExtensionViewHost* host,
views::View* anchor_view,
views::BubbleBorder::Arrow arrow,
ShowAction show_action)
: BubbleDialogDelegateView(anchor_view,
arrow,
views::BubbleBorder::SMALL_SHADOW),
host_(host) {
inspect_with_devtools_ = show_action == SHOW_AND_INSPECT;
set_margins(gfx::Insets());
SetLayoutManager(std::make_unique<views::FillLayout>());
AddChildView(GetExtensionView());
GetExtensionView()->set_container(this);
// ExtensionPopup closes itself on very specific de-activation conditions.
set_close_on_deactivate(false);
// Listen for the containing view calling window.close();
registrar_.Add(
this,
extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
content::Source<content::BrowserContext>(host->browser_context()));
content::DevToolsAgentHost::AddObserver(this);
GetExtensionView()->GetBrowser()->tab_strip_model()->AddObserver(this);
// If the host had somehow finished loading, then we'd miss the notification
// and not show. This seems to happen in single-process mode.
if (host_->has_loaded_once()) {
ShowBubble();
} else {
// Wait to show the popup until the contained host finishes loading.
registrar_.Add(this,
content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
content::Source<content::WebContents>(
host_->host_contents()));
}
}
ExtensionPopup::~ExtensionPopup() {
content::DevToolsAgentHost::RemoveObserver(this);
GetExtensionView()->GetBrowser()->tab_strip_model()->RemoveObserver(this);
}
int ExtensionPopup::GetDialogButtons() const {
return ui::DIALOG_BUTTON_NONE;
}
void ExtensionPopup::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME:
DCHECK_EQ(host()->host_contents(),
content::Source<content::WebContents>(source).ptr());
// Show when the content finishes loading and its width is computed.
ShowBubble();
break;
case extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE:
// If we aren't the host of the popup, then disregard the notification.
if (content::Details<extensions::ExtensionHost>(host()) == details)
GetWidget()->Close();
break;
default:
NOTREACHED() << "Received unexpected notification";
}
}
void ExtensionPopup::DevToolsAgentHostAttached(
content::DevToolsAgentHost* agent_host) {
// First check that the devtools are being opened on this popup.
if (host()->host_contents() != agent_host->GetWebContents())
return;
// Set inspect_with_devtools_ so the popup will be kept open while
// the devtools are open.
inspect_with_devtools_ = true;
}
void ExtensionPopup::DevToolsAgentHostDetached(
content::DevToolsAgentHost* agent_host) {
if (host()->host_contents() != agent_host->GetWebContents())
return;
inspect_with_devtools_ = false;
}
ExtensionViewViews* ExtensionPopup::GetExtensionView() {
return static_cast<ExtensionViewViews*>(host_.get()->view());
}
void ExtensionPopup::OnExtensionSizeChanged(ExtensionViewViews* view) {
SizeToContents();
}
gfx::Size ExtensionPopup::CalculatePreferredSize() const {
// Constrain the size to popup min/max.
gfx::Size sz = views::View::CalculatePreferredSize();
sz.set_width(std::max(kMinWidth, std::min(kMaxWidth, sz.width())));
sz.set_height(std::max(kMinHeight, std::min(kMaxHeight, sz.height())));
return sz;
}
void ExtensionPopup::AddedToWidget() {
const int radius =
GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius();
const bool contents_has_rounded_corners =
GetExtensionView()->holder()->SetCornerRadius(radius);
SetBorder(views::CreateEmptyBorder(
gfx::Insets(contents_has_rounded_corners ? 0 : radius, 0)));
}
void ExtensionPopup::OnWidgetActivationChanged(views::Widget* widget,
bool active) {
if (active && widget == anchor_widget())
CloseUnlessUnderInspection();
}
void ExtensionPopup::ActiveTabChanged(content::WebContents* old_contents,
content::WebContents* new_contents,
int index,
int reason) {
GetWidget()->Close();
}
void ExtensionPopup::CloseUnlessUnderInspection() {
if (!inspect_with_devtools_)
GetWidget()->Close();
}
// static
ExtensionPopup* ExtensionPopup::ShowPopup(
std::unique_ptr<extensions::ExtensionViewHost> host,
views::View* anchor_view,
views::BubbleBorder::Arrow arrow,
ShowAction show_action) {
return ExtensionPopup::Create(
host.release(), anchor_view, arrow, show_action);
}
void ExtensionPopup::ShowBubble() {
GetWidget()->Show();
// Focus on the host contents when the bubble is first shown.
host()->host_contents()->Focus();
if (inspect_with_devtools_) {
DevToolsWindow::OpenDevToolsWindow(
host()->host_contents(), DevToolsToggleAction::ShowConsolePanel());
}
}