blob: 2a35daaf8a19a4abd9e95a05a3841a6c420a4297 [file] [log] [blame]
// Copyright 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/frame/immersive_mode_controller_ash.h"
#include "ash/public/cpp/immersive/immersive_revealed_lock.h"
#include "ash/public/cpp/window_properties.h"
#include "base/macros.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/ui/ash/tablet_mode_client.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/top_container_view.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/web_contents.h"
#include "services/ws/public/cpp/property_type_converters.h"
#include "services/ws/public/mojom/window_manager.mojom.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/mus/mus_types.h"
#include "ui/aura/mus/property_converter.h"
#include "ui/aura/mus/window_port_mus.h"
#include "ui/aura/mus/window_tree_host_mus.h"
#include "ui/aura/window.h"
#include "ui/aura/window_targeter.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/paint_context.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/views/background.h"
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/view.h"
#include "ui/views/widget/native_widget_aura.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/non_client_view.h"
namespace {
// Converts from ImmersiveModeController::AnimateReveal to
// ash::ImmersiveFullscreenController::AnimateReveal.
ash::ImmersiveFullscreenController::AnimateReveal
ToImmersiveFullscreenControllerAnimateReveal(
ImmersiveModeController::AnimateReveal animate_reveal) {
switch (animate_reveal) {
case ImmersiveModeController::ANIMATE_REVEAL_YES:
return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_YES;
case ImmersiveModeController::ANIMATE_REVEAL_NO:
return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO;
}
NOTREACHED();
return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO;
}
class ImmersiveRevealedLockAsh : public ImmersiveRevealedLock {
public:
explicit ImmersiveRevealedLockAsh(ash::ImmersiveRevealedLock* lock)
: lock_(lock) {}
private:
std::unique_ptr<ash::ImmersiveRevealedLock> lock_;
DISALLOW_COPY_AND_ASSIGN(ImmersiveRevealedLockAsh);
};
} // namespace
ImmersiveModeControllerAsh::ImmersiveModeControllerAsh() = default;
ImmersiveModeControllerAsh::~ImmersiveModeControllerAsh() = default;
void ImmersiveModeControllerAsh::Init(BrowserView* browser_view) {
browser_view_ = browser_view;
controller_.Init(this, browser_view_->frame(),
browser_view_->top_container());
observed_windows_.Add(browser_view_->GetNativeWindow());
browser_view_->GetNativeWindow()->SetProperty(
ash::kImmersiveWindowType,
static_cast<int>(
browser_view_->browser()->is_app()
? ash::ImmersiveFullscreenController::WINDOW_TYPE_HOSTED_APP
: ash::ImmersiveFullscreenController::WINDOW_TYPE_BROWSER));
}
void ImmersiveModeControllerAsh::SetEnabled(bool enabled) {
if (controller_.IsEnabled() == enabled)
return;
if (registrar_.IsEmpty()) {
content::Source<FullscreenController> source(
browser_view_->browser()
->exclusive_access_manager()
->fullscreen_controller());
registrar_.Add(this, chrome::NOTIFICATION_FULLSCREEN_CHANGED, source);
}
ash::ImmersiveFullscreenController::EnableForWidget(browser_view_->frame(),
enabled);
}
bool ImmersiveModeControllerAsh::IsEnabled() const {
return controller_.IsEnabled();
}
bool ImmersiveModeControllerAsh::ShouldHideTopViews() const {
return controller_.IsEnabled() && !controller_.IsRevealed();
}
bool ImmersiveModeControllerAsh::IsRevealed() const {
return controller_.IsRevealed();
}
int ImmersiveModeControllerAsh::GetTopContainerVerticalOffset(
const gfx::Size& top_container_size) const {
if (!IsEnabled())
return 0;
return static_cast<int>(top_container_size.height() *
(visible_fraction_ - 1));
}
ImmersiveRevealedLock* ImmersiveModeControllerAsh::GetRevealedLock(
AnimateReveal animate_reveal) {
return new ImmersiveRevealedLockAsh(controller_.GetRevealedLock(
ToImmersiveFullscreenControllerAnimateReveal(animate_reveal)));
}
void ImmersiveModeControllerAsh::OnFindBarVisibleBoundsChanged(
const gfx::Rect& new_visible_bounds_in_screen) {
find_bar_visible_bounds_in_screen_ = new_visible_bounds_in_screen;
}
bool ImmersiveModeControllerAsh::ShouldStayImmersiveAfterExitingFullscreen() {
return !browser_view_->IsBrowserTypeNormal() &&
TabletModeClient::Get()->tablet_mode_enabled();
}
void ImmersiveModeControllerAsh::OnWidgetActivationChanged(
views::Widget* widget,
bool active) {
if (browser_view_->IsBrowserTypeNormal())
return;
if (!TabletModeClient::Get()->tablet_mode_enabled())
return;
// Don't use immersive mode as long as we are in the locked fullscreen mode
// since immersive shows browser controls which allow exiting the mode.
if (platform_util::IsBrowserLockedFullscreen(browser_view_->browser()))
return;
// Enable immersive mode if the widget is activated. Do not disable immersive
// mode if the widget deactivates, but is not minimized.
ash::ImmersiveFullscreenController::EnableForWidget(
browser_view_->frame(), active || !widget->IsMinimized());
}
void ImmersiveModeControllerAsh::LayoutBrowserRootView() {
views::Widget* widget = browser_view_->frame();
// Update the window caption buttons.
widget->non_client_view()->frame_view()->ResetWindowControls();
widget->non_client_view()->frame_view()->InvalidateLayout();
browser_view_->InvalidateLayout();
widget->GetRootView()->Layout();
}
void ImmersiveModeControllerAsh::OnImmersiveRevealStarted() {
visible_fraction_ = 0;
for (Observer& observer : observers_)
observer.OnImmersiveRevealStarted();
}
void ImmersiveModeControllerAsh::OnImmersiveRevealEnded() {
visible_fraction_ = 0;
browser_view_->contents_web_view()->holder()->SetHitTestTopInset(0);
for (Observer& observer : observers_)
observer.OnImmersiveRevealEnded();
}
void ImmersiveModeControllerAsh::OnImmersiveFullscreenEntered() {}
void ImmersiveModeControllerAsh::OnImmersiveFullscreenExited() {
browser_view_->contents_web_view()->holder()->SetHitTestTopInset(0);
for (Observer& observer : observers_)
observer.OnImmersiveFullscreenExited();
}
void ImmersiveModeControllerAsh::SetVisibleFraction(double visible_fraction) {
if (visible_fraction_ == visible_fraction)
return;
// Sets the top inset only when the top-of-window views is fully visible. This
// means some gesture may not be recognized well during the animation, but
// that's fine since a complicated gesture wouldn't be involved during the
// animation duration. See: https://crbug.com/901544.
if (browser_view_->IsBrowserTypeNormal()) {
if (visible_fraction == 1.0) {
browser_view_->contents_web_view()->holder()->SetHitTestTopInset(
browser_view_->top_container()->height());
} else if (visible_fraction_ == 1.0) {
browser_view_->contents_web_view()->holder()->SetHitTestTopInset(0);
}
}
visible_fraction_ = visible_fraction;
browser_view_->Layout();
}
std::vector<gfx::Rect> ImmersiveModeControllerAsh::GetVisibleBoundsInScreen()
const {
views::View* top_container_view = browser_view_->top_container();
gfx::Rect top_container_view_bounds = top_container_view->GetVisibleBounds();
// TODO(tdanderson): Implement View::ConvertRectToScreen().
gfx::Point top_container_view_bounds_in_screen_origin(
top_container_view_bounds.origin());
views::View::ConvertPointToScreen(
top_container_view, &top_container_view_bounds_in_screen_origin);
gfx::Rect top_container_view_bounds_in_screen(
top_container_view_bounds_in_screen_origin,
top_container_view_bounds.size());
std::vector<gfx::Rect> bounds_in_screen;
bounds_in_screen.push_back(top_container_view_bounds_in_screen);
bounds_in_screen.push_back(find_bar_visible_bounds_in_screen_);
return bounds_in_screen;
}
void ImmersiveModeControllerAsh::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type);
if (!controller_.IsEnabled())
return;
// Auto hide the shelf in immersive browser fullscreen.
bool in_tab_fullscreen = content::Source<FullscreenController>(source)
->IsWindowFullscreenForTabOrPending();
browser_view_->GetNativeWindow()->SetProperty(
ash::kHideShelfWhenFullscreenKey, in_tab_fullscreen);
}
void ImmersiveModeControllerAsh::OnWindowPropertyChanged(aura::Window* window,
const void* key,
intptr_t old) {
// Track locked fullscreen changes.
if (key == ash::kWindowPinTypeKey) {
browser_view_->FullscreenStateChanged();
return;
}
if (key == aura::client::kShowStateKey) {
ui::WindowShowState new_state =
window->GetProperty(aura::client::kShowStateKey);
auto old_state = static_cast<ui::WindowShowState>(old);
// Make sure the browser stays up to date with the window's state. This is
// necessary in classic Ash if the user exits fullscreen with the restore
// button, and it's necessary in OopAsh if the window manager initiates a
// fullscreen mode change (e.g. due to a WM shortcut).
if (new_state == ui::SHOW_STATE_FULLSCREEN ||
old_state == ui::SHOW_STATE_FULLSCREEN) {
// If the browser view initiated this state change,
// BrowserView::ProcessFullscreen will no-op, so this call is harmless.
browser_view_->FullscreenStateChanged();
}
}
}
void ImmersiveModeControllerAsh::OnWindowDestroying(aura::Window* window) {
// Clean up observers here rather than in the destructor because the owning
// BrowserView has already destroyed the aura::Window.
observed_windows_.Remove(window);
DCHECK(!observed_windows_.IsObservingSources());
}