// Copyright (c) 2011 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_view.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/ui/views/extensions/extension_popup.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/renderer_host/render_widget_host_view.h"
#include "views/widget/widget.h"
#if defined(OS_WIN)
#include "chrome/browser/renderer_host/render_widget_host_view_win.h"
#elif defined(TOUCH_UI)
#include "chrome/browser/renderer_host/render_widget_host_view_views.h"
#elif defined(OS_LINUX)
#include "chrome/browser/renderer_host/render_widget_host_view_gtk.h"
#endif
ExtensionView::ExtensionView(ExtensionHost* host, Browser* browser)
: host_(host),
browser_(browser),
initialized_(false),
container_(NULL),
is_clipped_(false) {
host_->set_view(this);
// This view needs to be focusable so it can act as the focused view for the
// focus manager. This is required to have SkipDefaultKeyEventProcessing
// called so the tab key events are forwarded to the renderer.
SetFocusable(true);
}
ExtensionView::~ExtensionView() {
if (parent())
parent()->RemoveChildView(this);
CleanUp();
}
const Extension* ExtensionView::extension() const {
return host_->extension();
}
RenderViewHost* ExtensionView::render_view_host() const {
return host_->render_view_host();
}
void ExtensionView::DidStopLoading() {
ShowIfCompletelyLoaded();
}
void ExtensionView::SetIsClipped(bool is_clipped) {
if (is_clipped_ != is_clipped) {
is_clipped_ = is_clipped;
if (IsVisible())
ShowIfCompletelyLoaded();
}
}
void ExtensionView::SetVisible(bool is_visible) {
if (is_visible != IsVisible()) {
NativeViewHost::SetVisible(is_visible);
// Also tell RenderWidgetHostView the new visibility. Despite its name, it
// is not part of the View hierarchy and does not know about the change
// unless we tell it.
if (render_view_host()->view()) {
if (is_visible)
render_view_host()->view()->Show();
else
render_view_host()->view()->Hide();
}
}
}
void ExtensionView::CreateWidgetHostView() {
DCHECK(!initialized_);
initialized_ = true;
RenderWidgetHostView* view =
RenderWidgetHostView::CreateViewForWidget(render_view_host());
// TODO(mpcomplete): RWHV needs a cross-platform Init function.
#if defined(OS_WIN)
// Create the HWND. Note:
// RenderWidgetHostHWND supports windowed plugins, but if we ever also
// wanted to support constrained windows with this, we would need an
// additional HWND to parent off of because windowed plugin HWNDs cannot
// exist in the same z-order as constrained windows.
RenderWidgetHostViewWin* view_win =
static_cast<RenderWidgetHostViewWin*>(view);
HWND hwnd = view_win->Create(GetWidget()->GetNativeView());
view_win->ShowWindow(SW_SHOW);
Attach(hwnd);
#elif defined(TOUCH_UI)
RenderWidgetHostViewViews* view_views =
static_cast<RenderWidgetHostViewViews*>(view);
view_views->InitAsChild();
AttachToView(view_views);
#elif defined(OS_LINUX)
RenderWidgetHostViewGtk* view_gtk =
static_cast<RenderWidgetHostViewGtk*>(view);
view_gtk->InitAsChild();
Attach(view_gtk->GetNativeView());
#else
NOTIMPLEMENTED();
#endif
host_->CreateRenderViewSoon(view);
SetVisible(false);
}
void ExtensionView::ShowIfCompletelyLoaded() {
if (IsVisible() || is_clipped_)
return;
// We wait to show the ExtensionView until it has loaded, and the view has
// actually been created. These can happen in different orders.
if (host_->did_stop_loading()) {
SetVisible(true);
UpdatePreferredSize(pending_preferred_size_);
}
}
void ExtensionView::CleanUp() {
if (!initialized_)
return;
if (native_view())
Detach();
initialized_ = false;
}
void ExtensionView::SetBackground(const SkBitmap& background) {
if (render_view_host()->IsRenderViewLive() && render_view_host()->view()) {
render_view_host()->view()->SetBackground(background);
} else {
pending_background_ = background;
}
ShowIfCompletelyLoaded();
}
void ExtensionView::UpdatePreferredSize(const gfx::Size& new_size) {
// Don't actually do anything with this information until we have been shown.
// Size changes will not be honored by lower layers while we are hidden.
if (!IsVisible()) {
pending_preferred_size_ = new_size;
return;
}
gfx::Size preferred_size = GetPreferredSize();
if (new_size != preferred_size)
SetPreferredSize(new_size);
}
void ExtensionView::ViewHierarchyChanged(bool is_add,
views::View *parent,
views::View *child) {
NativeViewHost::ViewHierarchyChanged(is_add, parent, child);
if (is_add && GetWidget() && !initialized_)
CreateWidgetHostView();
}
void ExtensionView::PreferredSizeChanged() {
View::PreferredSizeChanged();
if (container_)
container_->OnExtensionPreferredSizeChanged(this);
}
bool ExtensionView::SkipDefaultKeyEventProcessing(const views::KeyEvent& e) {
// Let the tab key event be processed by the renderer (instead of moving the
// focus to the next focusable view).
return (e.key_code() == ui::VKEY_TAB);
}
void ExtensionView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
// Propagate the new size to RenderWidgetHostView.
// We can't send size zero because RenderWidget DCHECKs that.
if (render_view_host()->view() && !bounds().IsEmpty())
render_view_host()->view()->SetSize(size());
}
void ExtensionView::HandleMouseMove() {
if (container_)
container_->OnExtensionMouseMove(this);
}
void ExtensionView::HandleMouseLeave() {
if (container_)
container_->OnExtensionMouseLeave(this);
}
void ExtensionView::RenderViewCreated() {
if (!pending_background_.empty() && render_view_host()->view()) {
render_view_host()->view()->SetBackground(pending_background_);
pending_background_.reset();
}
// Tell the renderer not to draw scroll bars in popups unless the
// popups are at the maximum allowed size.
gfx::Size largest_popup_size(ExtensionPopup::kMaxWidth,
ExtensionPopup::kMaxHeight);
host_->DisableScrollbarsForSmallWindows(largest_popup_size);
}