Skip to content

Commit

Permalink
[win] Make the HTML5 fullscreen API work in system apps.
Browse files Browse the repository at this point in the history
R=ben@chromium.org,bbudge@chromium.org
BUG=130463


Review URL: https://chromiumcodereview.appspot.com/10444131

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@140472 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
jeremya@chromium.org committed Jun 5, 2012
1 parent 3fcbd4b commit 84aa895
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 89 deletions.
21 changes: 21 additions & 0 deletions chrome/browser/ui/extensions/shell_window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ ShellWindow::ShellWindow(Profile* profile,
extension_(extension),
ALLOW_THIS_IN_INITIALIZER_LIST(
extension_function_dispatcher_(profile, this)) {
// TODO(jeremya) this should all be done in an Init() method, not in the
// constructor. During this code, WebContents will be calling
// WebContentsDelegate methods, but at this point the vftables for the
// subclass are not yet in place, since it's still halfway through its
// constructor. As a result, overridden virtual methods won't be called.
web_contents_ = WebContents::Create(
profile, SiteInstance::CreateForURL(profile, url), MSG_ROUTING_NONE, NULL,
NULL);
Expand Down Expand Up @@ -162,6 +167,10 @@ ShellWindow::~ShellWindow() {
browser::EndKeepAlive();
}

bool ShellWindow::IsFullscreenOrPending() const {
return false;
}

string16 ShellWindow::GetTitle() const {
// WebContents::GetTitle() will return the page's URL if there's no <title>
// specified. However, we'd prefer to show the name of the extension in that
Expand Down Expand Up @@ -223,6 +232,18 @@ void ShellWindow::NavigationStateChanged(
UpdateWindowTitle();
}

void ShellWindow::ToggleFullscreenModeForTab(content::WebContents* source,
bool enter_fullscreen) {
DCHECK(source == web_contents_);
SetFullscreen(enter_fullscreen);
}

bool ShellWindow::IsFullscreenForTabOrPending(
const content::WebContents* source) const {
DCHECK(source == web_contents_);
return IsFullscreenOrPending();
}

void ShellWindow::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
Expand Down
7 changes: 7 additions & 0 deletions chrome/browser/ui/extensions/shell_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class ShellWindow : public content::NotificationObserver,
// startup and from within UpdateWindowTitle().
virtual string16 GetTitle() const;

virtual void SetFullscreen(bool fullscreen) {}
virtual bool IsFullscreenOrPending() const;

private:
// PlatformAppBrowserTest needs access to web_contents()
friend class PlatformAppBrowserTest;
Expand Down Expand Up @@ -105,6 +108,10 @@ class ShellWindow : public content::NotificationObserver,
content::WebContents* source, const gfx::Rect& pos) OVERRIDE;
virtual void NavigationStateChanged(const content::WebContents* source,
unsigned changed_flags) OVERRIDE;
virtual void ToggleFullscreenModeForTab(content::WebContents* source,
bool enter_fullscreen) OVERRIDE;
virtual bool IsFullscreenForTabOrPending(
const content::WebContents* source) const OVERRIDE;

// content::NotificationObserver implementation.
virtual void Observe(int type,
Expand Down
227 changes: 150 additions & 77 deletions chrome/browser/ui/views/extensions/shell_window_views.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "grit/ui_resources_standard.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "ui/base/hit_test.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/path.h"
#include "ui/gfx/scoped_sk_region.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/non_client_view.h"

Expand All @@ -31,6 +38,8 @@
// Number of pixels around the edge of the window that can be dragged to
// resize the window.
static const int kResizeBorderWidth = 5;
// Height of the chrome-style caption, in pixels.
static const int kCaptionHeight = 25;

class ShellWindowFrameView : public views::NonClientFrameView {
public:
Expand Down Expand Up @@ -106,20 +115,84 @@ void ShellWindowFrameView::GetWindowMask(const gfx::Size& size,
// Don't touch it.
}

namespace {

class TitleBarBackground : public views::Background {
public:
TitleBarBackground() {}
virtual ~TitleBarBackground() {}
virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
SkPaint paint;
paint.setAntiAlias(false);
paint.setStyle(SkPaint::kFill_Style);
paint.setColor(SK_ColorWHITE);
gfx::Path path;
int radius = 1;
int width = view->width();
int height = view->height();
path.moveTo(0, radius);
path.lineTo(radius, 0);
path.lineTo(width - radius - 1, 0);
path.lineTo(width, radius + 1);
path.lineTo(width, height);
path.lineTo(0, height);
path.close();
canvas->DrawPath(path, paint);
}
};

class TitleBarView : public views::View {
public:
explicit TitleBarView(views::ButtonListener* listener);
virtual ~TitleBarView();
};

TitleBarView::TitleBarView(views::ButtonListener* listener) {
set_background(new TitleBarBackground());
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
views::ImageButton* close_button = new views::ImageButton(listener);
close_button->SetImage(views::CustomButton::BS_NORMAL,
rb.GetNativeImageNamed(IDR_CLOSE_BAR).ToImageSkia());
close_button->SetImage(views::CustomButton::BS_HOT,
rb.GetNativeImageNamed(IDR_CLOSE_BAR_H).ToImageSkia());
close_button->SetImage(views::CustomButton::BS_PUSHED,
rb.GetNativeImageNamed(IDR_CLOSE_BAR_P).ToImageSkia());
views::GridLayout* layout = new views::GridLayout(this);
layout->SetInsets(0, 0, 0,
(kCaptionHeight - close_button->GetPreferredSize().height()) / 2);
SetLayoutManager(layout);
views::ColumnSet* columns = layout->AddColumnSet(0);
columns->AddPaddingColumn(1, 1);
columns->AddColumn(
views::GridLayout::TRAILING, views::GridLayout::CENTER, 0,
views::GridLayout::USE_PREF, 0, 0);
layout->StartRow(1, 0);
layout->AddView(close_button);
}

TitleBarView::~TitleBarView() {
}

}; // namespace

ShellWindowViews::ShellWindowViews(Profile* profile,
const extensions::Extension* extension,
const GURL& url,
const ShellWindow::CreateParams& win_params)
: ShellWindow(profile, extension, url),
initialized_(false),
title_view_(NULL),
web_view_(NULL),
is_fullscreen_(false),
use_custom_frame_(
win_params.frame == ShellWindow::CreateParams::FRAME_CUSTOM) {
window_ = new views::Widget;
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.delegate = this;
params.remove_standard_frame = use_custom_frame_;
params.remove_standard_frame = true;
params.bounds = win_params.bounds;
minimum_size_ = win_params.minimum_size;
if (!use_custom_frame_)
params.bounds.set_height(params.bounds.height() + kCaptionHeight);
window_->Init(params);
#if defined(OS_WIN) && !defined(USE_AURA)
std::string app_name = web_app::GenerateApplicationNameFromExtensionId(
Expand All @@ -129,13 +202,37 @@ ShellWindowViews::ShellWindowViews(Profile* profile,
profile->GetPath()),
GetWidget()->GetTopLevelWidget()->GetNativeWindow());
#endif
SetLayoutManager(new views::FillLayout);
Layout();
OnViewWasResized();

window_->Show();
}

void ShellWindowViews::ViewHierarchyChanged(
bool is_add, views::View *parent, views::View *child) {
if (is_add && child == this) {
if (!use_custom_frame_) {
title_view_ = new TitleBarView(this);
AddChildView(title_view_);
}
web_view_ = new views::WebView(NULL);
AddChildView(web_view_);
web_view_->SetWebContents(web_contents());
}
}

void ShellWindowViews::SetFullscreen(bool fullscreen) {
is_fullscreen_ = fullscreen;
window_->SetFullscreen(fullscreen);
// TODO(jeremya) we need to call RenderViewHost::ExitFullscreen() if we
// ever drop the window out of fullscreen in response to something that
// wasn't the app calling webkitCancelFullScreen().
}

bool ShellWindowViews::IsFullscreenOrPending() const {
return is_fullscreen_;
}

ShellWindowViews::~ShellWindowViews() {
if (initialized_)
NativeViewHost::Detach();
}

bool ShellWindowViews::IsActive() const {
Expand Down Expand Up @@ -222,6 +319,11 @@ void ShellWindowViews::DeleteDelegate() {
delete this;
}

void ShellWindowViews::ButtonPressed(
views::Button* sender, const views::Event& event) {
Close();
}

bool ShellWindowViews::CanResize() const {
return true;
}
Expand All @@ -243,9 +345,7 @@ views::NonClientFrameView* ShellWindowViews::CreateNonClientFrameView(
frame->Init(widget);
return frame;
#else
if (use_custom_frame_)
return new ShellWindowFrameView(this);
return NULL;
return new ShellWindowFrameView(this);
#endif
}

Expand All @@ -265,93 +365,66 @@ void ShellWindowViews::OnViewWasResized() {
// TODO(jeremya): this doesn't seem like a terribly elegant way to keep the
// window shape in sync.
#if defined(OS_WIN) && !defined(USE_AURA)
if (!use_custom_frame_)
return;
gfx::Size sz = size();
// Set the window shape of the RWHV.
gfx::Size sz = web_view_->size();
int height = sz.height(), width = sz.width();
int radius = 1;
gfx::Path path;
if (GetWidget()->IsMaximized()) {
// Don't round the corners when the window is maximized.
if (window_->IsMaximized() || window_->IsFullscreen()) {
// Don't round the corners when the window is maximized or fullscreen.
path.addRect(0, 0, width, height);
} else {
path.moveTo(0, radius);
path.lineTo(radius, 0);
path.lineTo(width - radius, 0);
path.lineTo(width, radius);
if (use_custom_frame_) {
path.moveTo(0, radius);
path.lineTo(radius, 0);
path.lineTo(width - radius, 0);
path.lineTo(width, radius);
} else {
// Don't round the top corners in chrome-style frame mode.
path.moveTo(0, 0);
path.lineTo(width, 0);
}
path.lineTo(width, height - radius - 1);
path.lineTo(width - radius - 1, height);
path.lineTo(radius + 1, height);
path.lineTo(0, height - radius - 1);
path.close();
}
SetWindowRgn(native_view(), path.CreateNativeRegion(), 1);
SetWindowRgn(web_contents()->GetNativeView(), path.CreateNativeRegion(), 1);

SkRegion* rgn = new SkRegion;
if (caption_region_.Get())
rgn->op(*caption_region_.Get(), SkRegion::kUnion_Op);
if (!GetWidget()->IsMaximized()) {
rgn->op(0, 0, width, kResizeBorderWidth, SkRegion::kUnion_Op);
rgn->op(0, 0, kResizeBorderWidth, height, SkRegion::kUnion_Op);
rgn->op(width - kResizeBorderWidth, 0, width, height, SkRegion::kUnion_Op);
rgn->op(0, height - kResizeBorderWidth, width, height, SkRegion::kUnion_Op);
if (!window_->IsFullscreen()) {
if (caption_region_.Get())
rgn->op(*caption_region_.Get(), SkRegion::kUnion_Op);
if (!window_->IsMaximized()) {
if (use_custom_frame_)
rgn->op(0, 0, width, kResizeBorderWidth, SkRegion::kUnion_Op);
rgn->op(0, 0, kResizeBorderWidth, height, SkRegion::kUnion_Op);
rgn->op(width - kResizeBorderWidth, 0, width, height,
SkRegion::kUnion_Op);
rgn->op(0, height - kResizeBorderWidth, width, height,
SkRegion::kUnion_Op);
}
}
web_contents()->GetRenderViewHost()->GetView()->SetClickthroughRegion(rgn);
#endif
}

gfx::NativeCursor ShellWindowViews::GetCursor(const views::MouseEvent& event) {
return gfx::kNullCursor;
}

void ShellWindowViews::SetVisible(bool is_visible) {
if (is_visible != visible()) {
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.
content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
if (rvh->GetView()) {
if (is_visible)
rvh->GetView()->Show();
else
rvh->GetView()->Hide();
void ShellWindowViews::Layout() {
if (use_custom_frame_) {
web_view_->SetBounds(0, 0, width(), height());
} else {
if (window_->IsFullscreen()) {
title_view_->SetVisible(false);
web_view_->SetBounds(0, 0, width(), height());
} else {
title_view_->SetVisible(true);
title_view_->SetBounds(0, 0, width(), kCaptionHeight);
web_view_->SetBounds(0, kCaptionHeight,
width(), height() - kCaptionHeight);
}
}
}

void ShellWindowViews::ViewHierarchyChanged(bool is_add,
views::View *parent,
views::View *child) {
NativeViewHost::ViewHierarchyChanged(is_add, parent, child);
if (is_add && GetWidget() && !initialized_) {
initialized_ = true;
NativeViewHost::Attach(web_contents()->GetView()->GetNativeView());
}
}

void ShellWindowViews::PreferredSizeChanged() {
View::PreferredSizeChanged();
}

bool ShellWindowViews::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). Also handle Backspace, since otherwise
// (on Windows at least), pressing Backspace, when focus is on a text field
// within the ExtensionView, will navigate the page back instead of erasing a
// character.
return (e.key_code() == ui::VKEY_TAB || e.key_code() == ui::VKEY_BACK);
}

void ShellWindowViews::OnBoundsChanged(const gfx::Rect& previous_bounds) {
// Propagate the new size to RenderWidgetHostView.
// We can't send size zero because RenderWidget DCHECKs that.
content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
if (rvh->GetView() && !bounds().IsEmpty()) {
rvh->GetView()->SetSize(size());
OnViewWasResized();
}
OnViewWasResized();
}

void ShellWindowViews::UpdateWindowTitle() {
Expand Down
Loading

0 comments on commit 84aa895

Please sign in to comment.