Commit 0b22a424 authored by Evan Stade's avatar Evan Stade Committed by Commit Bot

Mash: Support Chrome App immersive mode

The client-side window frame/client layout code uses a per-window
property (the pre-existing kTopViewInset) to layout the contents,
rather than hardcoded values plus logic related to window state. This
is necessary so that apps will be laid out appropriately in immersive
mode when not fullscreened (i.e. maximized in tablet mode).

Also, propagate some window properties across processes.

Bug: 640365
Change-Id: I485920c242e1c98450d0868e078f534489525f7c
Reviewed-on: https://chromium-review.googlesource.com/1195886Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Commit-Queue: Evan Stade <estade@chromium.org>
Cr-Commit-Position: refs/heads/master@{#588894}
parent 41fba725
......@@ -17,6 +17,7 @@
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_state.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/mus/window_tree_host_mus.h"
#include "ui/aura/window.h"
#include "ui/base/ui_base_features.h"
#include "ui/views/controls/image_view.h"
......@@ -312,7 +313,10 @@ FrameCaptionButton* HeaderView::GetBackButton() {
void HeaderView::OnImmersiveRevealStarted() {
fullscreen_visible_fraction_ = 0;
SetPaintToLayer();
add_layer_for_immersive_ = !layer();
if (add_layer_for_immersive_)
SetPaintToLayer();
// AppWindow may call this before being added to the widget.
// https://crbug.com/825260.
if (layer()->parent()) {
......@@ -324,20 +328,26 @@ void HeaderView::OnImmersiveRevealStarted() {
void HeaderView::OnImmersiveRevealEnded() {
fullscreen_visible_fraction_ = 0;
DestroyLayer();
if (add_layer_for_immersive_)
DestroyLayer();
parent()->Layout();
}
void HeaderView::OnImmersiveFullscreenEntered() {
in_immersive_mode_ = true;
parent()->InvalidateLayout();
// The frame may not have been created yet (during window initialization).
if (target_widget_ && target_widget_->non_client_view()->frame_view())
target_widget_->non_client_view()->Layout();
}
void HeaderView::OnImmersiveFullscreenExited() {
in_immersive_mode_ = false;
fullscreen_visible_fraction_ = 0;
DestroyLayer();
if (add_layer_for_immersive_)
DestroyLayer();
parent()->InvalidateLayout();
if (target_widget_)
if (target_widget_ && target_widget_->non_client_view()->frame_view())
target_widget_->non_client_view()->Layout();
}
......
......@@ -155,6 +155,11 @@ class ASH_EXPORT HeaderView : public views::View,
// Has this instance been set as the ImmersiveFullscreenControllerDelegate?
bool is_immersive_delegate_ = true;
// True if a layer should be used for the immersive mode reveal. Some code
// needs HeaderView to always paint to a layer instead of only during
// immersive reveal (see WmNativeWidgetAura).
bool add_layer_for_immersive_ = false;
bool did_layout_ = false;
// False to skip painting. Used for overview mode to hide the header.
......
......@@ -237,7 +237,6 @@ const char NonClientFrameViewAsh::kViewClassName[] = "NonClientFrameViewAsh";
NonClientFrameViewAsh::NonClientFrameViewAsh(
views::Widget* frame,
ImmersiveFullscreenControllerDelegate* immersive_delegate,
bool control_immersive,
mojom::WindowStyle window_style,
std::unique_ptr<CaptionButtonModel> model)
: frame_(frame),
......@@ -257,7 +256,7 @@ NonClientFrameViewAsh::NonClientFrameViewAsh(
wm::WindowState* window_state = wm::GetWindowState(frame_window);
if (!window_state->HasDelegate()) {
immersive_helper_ = std::make_unique<NonClientFrameViewAshImmersiveHelper>(
frame, this, control_immersive);
frame, this, !immersive_delegate);
}
Shell::Get()->AddShellObserver(this);
Shell::Get()->split_view_controller()->AddObserver(this);
......
......@@ -52,7 +52,6 @@ class ASH_EXPORT NonClientFrameViewAsh : public views::NonClientFrameView,
explicit NonClientFrameViewAsh(
views::Widget* frame,
ImmersiveFullscreenControllerDelegate* immersive_delegate = nullptr,
bool control_immersive = true,
mojom::WindowStyle window_style = mojom::WindowStyle::DEFAULT,
std::unique_ptr<CaptionButtonModel> model = nullptr);
~NonClientFrameViewAsh() override;
......
......@@ -61,7 +61,7 @@ class NonClientFrameViewAshTestWidgetDelegate
views::NonClientFrameView* CreateNonClientFrameView(
views::Widget* widget) override {
non_client_frame_view_ =
new NonClientFrameViewAsh(widget, nullptr, true, window_style_);
new NonClientFrameViewAsh(widget, nullptr, window_style_);
return non_client_frame_view_;
}
......
......@@ -71,6 +71,10 @@ void MusPropertyMirrorAsh::MirrorPropertyFromWidgetWindowToRootWindow(
} else if (key == aura::client::kTitleShownKey) {
root_window->SetProperty(aura::client::kTitleShownKey,
window->GetProperty(aura::client::kTitleShownKey));
} else if (key == aura::client::kTopViewInset) {
root_window->SetProperty(aura::client::kTopViewInset,
window->GetProperty(aura::client::kTopViewInset));
} else if (key == aura::client::kWindowIconKey) {
MirrorOwnedProperty(window, root_window, aura::client::kWindowIconKey);
} else if (key == kFrameBackButtonStateKey) {
......@@ -103,6 +107,16 @@ void MusPropertyMirrorAsh::MirrorPropertyFromWidgetWindowToRootWindow(
root_window->SetProperty(
kHideCaptionButtonsInTabletModeKey,
window->GetProperty(kHideCaptionButtonsInTabletModeKey));
} else if (key == kImmersiveImpliedByFullscreen) {
root_window->SetProperty(
kImmersiveImpliedByFullscreen,
window->GetProperty(kImmersiveImpliedByFullscreen));
} else if (key == kImmersiveIsActive) {
root_window->SetProperty(kImmersiveIsActive,
window->GetProperty(kImmersiveIsActive));
} else if (key == kImmersiveTopContainerBoundsInScreen) {
MirrorOwnedProperty(window, root_window,
kImmersiveTopContainerBoundsInScreen);
}
}
......
......@@ -82,6 +82,16 @@ void RegisterWindowProperties(aura::PropertyConverter* property_converter) {
property_converter->RegisterPrimitiveProperty(
kHideShelfWhenFullscreenKey, mojom::kHideShelfWhenFullscreen_Property,
aura::PropertyConverter::CreateAcceptAnyValueCallback());
property_converter->RegisterPrimitiveProperty(
kImmersiveImpliedByFullscreen,
mojom::kImmersiveImpliedByFullscreen_Property,
aura::PropertyConverter::CreateAcceptAnyValueCallback());
property_converter->RegisterPrimitiveProperty(
kImmersiveIsActive, mojom::kImmersiveIsActive_Property,
aura::PropertyConverter::CreateAcceptAnyValueCallback());
property_converter->RegisterRectProperty(
kImmersiveTopContainerBoundsInScreen,
mojom::kImmersiveTopContainerBoundsInScreen_Property);
property_converter->RegisterPrimitiveProperty(
kIsDeferredTabDraggingTargetWindowKey,
mojom::kIsDeferredTabDraggingTargetWindow_Property,
......@@ -102,6 +112,9 @@ void RegisterWindowProperties(aura::PropertyConverter* property_converter) {
property_converter->RegisterPrimitiveProperty(
kShelfItemTypeKey, ws::mojom::WindowManager::kShelfItemType_Property,
base::BindRepeating(&IsValidShelfItemType));
property_converter->RegisterPrimitiveProperty(
aura::client::kTopViewInset, mojom::kTopViewInset_Property,
aura::PropertyConverter::CreateAcceptAnyValueCallback());
property_converter->RegisterPrimitiveProperty(
kWindowStateTypeKey, mojom::kWindowStateType_Property,
base::BindRepeating(&IsValidWindowStateType));
......
......@@ -55,6 +55,17 @@ const string kHideCaptionButtonsInTabletMode_Property =
const string kHideShelfWhenFullscreen_Property =
"ash:hide-shelf-when-fullscreen";
// See ash::kImmseriveImpliedByFullscreen.
const string kImmersiveImpliedByFullscreen_Property =
"ash:immersive-implied-by-fullscreen";
// See ash::kImmseriveIsActive.
const string kImmersiveIsActive_Property = "ash:immersive-is-active";
// See ash::kImmseriveTopContainerBoundsInScreen.
const string kImmersiveTopContainerBoundsInScreen_Property =
"ash:immersive-top-container-bounds-in-screen";
// If true, the window is the target window for the tab-dragged window. The
// key is used by overview to show a highlight indication to indicate which
// overview window the dragged tabs will merge into when the user releases the
......@@ -92,3 +103,7 @@ const string kRestoreBoundsOverride_Property =
// mode window manager.
const string kRestoreWindowStateTypeOverride_Property =
"ash:restore-window-state-type-override";
// The height of the window header, i.e. the inset between the frame and client
// view.
const string kTopViewInset_Property = "ash:top-view-inset";
......@@ -185,11 +185,6 @@ class WmNativeWidgetAura : public views::NativeWidgetAura {
window_style_(window_style) {}
~WmNativeWidgetAura() override = default;
void SetHeaderHeight(int height) {
if (custom_frame_view_)
custom_frame_view_->SetHeaderHeight({height});
}
void set_cursor(const ui::Cursor& cursor) { cursor_ = cursor; }
// views::NativeWidgetAura:
......@@ -199,13 +194,16 @@ class WmNativeWidgetAura : public views::NativeWidgetAura {
if (remove_standard_frame_)
return new EmptyDraggableNonClientFrameView();
aura::Window* window = GetNativeView();
immersive_delegate_ =
std::make_unique<ImmersiveFullscreenControllerDelegateMus>(GetWidget(),
window);
if (!enable_immersive_) {
immersive_delegate_ =
std::make_unique<ImmersiveFullscreenControllerDelegateMus>(
GetWidget(), window);
}
// See description for details on ownership.
custom_frame_view_ =
new NonClientFrameViewAsh(GetWidget(), immersive_delegate_.get(),
enable_immersive_, window_style_);
custom_frame_view_ = new NonClientFrameViewAsh(
GetWidget(), immersive_delegate_.get(), window_style_);
// Only the header actually paints any content. So the rest of the region is
// marked as transparent content (see below in NonClientFrameController()
......@@ -368,11 +366,6 @@ int NonClientFrameController::GetMaxTitleBarButtonWidth() {
return GetAshLayoutSize(AshLayoutSize::kNonBrowserCaption).width() * 3;
}
void NonClientFrameController::SetClientArea(const gfx::Insets& insets) {
static_cast<WmNativeWidgetAura*>(widget_->native_widget())
->SetHeaderHeight(insets.top());
}
void NonClientFrameController::StoreCursor(const ui::Cursor& cursor) {
static_cast<WmNativeWidgetAura*>(widget_->native_widget())
->set_cursor(cursor);
......
......@@ -72,8 +72,6 @@ class ASH_EXPORT NonClientFrameController
aura::Window* window() { return window_; }
void SetClientArea(const gfx::Insets& insets);
// Stores |cursor| as this window's active cursor. It does not actually update
// the active cursor by calling into CursorManager, but will update the return
// value provided by the associated window's aura::WindowDelegate::GetCursor.
......
......@@ -3,10 +3,9 @@ include_rules = [
]
specific_include_rules = {
# TODO(mash): Fix. https://crbug.com/855096
# These are only used in non-Mash configs.
"chrome_native_app_window_views_aura_ash\.*": [
"+ash/frame/non_client_frame_view_ash.h",
"+ash/wm/window_properties.h",
"+ash/wm/window_state.h",
"+ash/wm/window_state_observer.h",
],
......
......@@ -7,7 +7,7 @@
#include <utility>
#include "apps/ui/views/app_window_frame_view.h"
#include "ash/frame/non_client_frame_view_ash.h"
#include "ash/frame/non_client_frame_view_ash.h" // mash-ok
#include "ash/public/cpp/app_types.h"
#include "ash/public/cpp/ash_constants.h"
#include "ash/public/cpp/ash_switches.h"
......@@ -16,8 +16,7 @@
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/public/cpp/window_state_type.h"
#include "ash/wm/window_properties.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_state.h" // mash-ok
#include "base/logging.h"
#include "chrome/browser/chromeos/note_taking_helper.h"
#include "chrome/browser/profiles/profile.h"
......@@ -36,6 +35,7 @@
#include "ui/base/hit_test.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/ui_base_types.h"
#include "ui/display/screen.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/keyboard_codes.h"
......@@ -72,6 +72,9 @@ void ChromeNativeAppWindowViewsAuraAsh::InitializeWindow(
// Fullscreen doesn't always imply immersive mode (see
// ShouldEnableImmersive()).
window->SetProperty(ash::kImmersiveImpliedByFullscreen, false);
observed_window_.Add(
features::IsUsingWindowService() ? window->GetRootWindow() : window);
}
///////////////////////////////////////////////////////////////////////////////
......@@ -215,17 +218,12 @@ void ChromeNativeAppWindowViewsAuraAsh::ShowContextMenuForView(
views::NonClientFrameView*
ChromeNativeAppWindowViewsAuraAsh::CreateNonClientFrameView(
views::Widget* widget) {
observed_window_.Add(GetNativeWindow());
if (IsFrameless())
return CreateNonStandardAppFrame();
if (features::IsUsingWindowService())
return nullptr;
// TODO(estade): WindowState is not available in OopAsh, so observe changes to
// the window's kWindowStateTypeKey instead. This isn't possible in classic
// Ash; see comment in OnPostWindowStateTypeChange().
observed_window_state_.Add(ash::wm::GetWindowState(GetNativeWindow()));
ash::NonClientFrameViewAsh* custom_frame_view =
......@@ -461,6 +459,7 @@ void ChromeNativeAppWindowViewsAuraAsh::OnWidgetActivationChanged(
void ChromeNativeAppWindowViewsAuraAsh::OnPostWindowStateTypeChange(
ash::wm::WindowState* window_state,
ash::mojom::WindowStateType old_type) {
DCHECK(!features::IsUsingWindowService());
DCHECK_EQ(GetNativeWindow(), window_state->window());
if (window_state->IsFullscreen() != app_window()->IsFullscreen()) {
// Report OS-initiated state changes to |app_window()|. This is done in
......@@ -479,22 +478,26 @@ void ChromeNativeAppWindowViewsAuraAsh::OnWindowPropertyChanged(
aura::Window* window,
const void* key,
intptr_t old) {
if (key == ash::kWindowStateTypeKey) {
ash::mojom::WindowStateType new_state =
window->GetProperty(ash::kWindowStateTypeKey);
if (new_state != ash::mojom::WindowStateType::FULLSCREEN &&
new_state != ash::mojom::WindowStateType::MINIMIZED &&
app_window()->GetBaseWindow() &&
app_window()->GetBaseWindow()->IsFullscreenOrPending()) {
app_window()->Restore();
// Usually OnNativeWindowChanged() is called when the window bounds are
// changed as a result of a state type change. Because the change in
// state type has already occurred, we need to call
// OnNativeWindowChanged() explicitly.
app_window()->OnNativeWindowChanged();
}
if (key != aura::client::kShowStateKey)
return;
auto new_state = window->GetProperty(aura::client::kShowStateKey);
if (new_state != ui::SHOW_STATE_FULLSCREEN &&
new_state != ui::SHOW_STATE_MINIMIZED && app_window()->IsFullscreen()) {
app_window()->Restore();
} else if (features::IsUsingWindowService() &&
new_state == ui::SHOW_STATE_FULLSCREEN &&
!app_window()->IsFullscreen()) {
app_window()->OSFullscreen();
}
// Usually OnNativeWindowChanged() is called when the window bounds are
// changed as a result of a state type change. Because the change in
// state type has already occurred, we need to call
// OnNativeWindowChanged() explicitly.
app_window()->OnNativeWindowChanged();
UpdateImmersiveMode();
}
void ChromeNativeAppWindowViewsAuraAsh::OnWindowDestroying(
......
......@@ -8,7 +8,7 @@
#include <memory>
#include <vector>
#include "ash/wm/window_state_observer.h"
#include "ash/wm/window_state_observer.h" // mash-ok
#include "base/gtest_prod_util.h"
#include "base/scoped_observer.h"
#include "chrome/browser/ui/ash/tablet_mode_client_observer.h"
......
......@@ -88,7 +88,7 @@ IN_PROC_BROWSER_TEST_F(ChromeNativeAppWindowViewsAuraAshBrowserTest,
EXPECT_FALSE(IsImmersiveActive());
ASSERT_NO_FATAL_FAILURE(test::SetAndWaitForTabletMode(true));
EXPECT_FALSE(IsImmersiveActive());
window()->Show();
window()->Restore();
EXPECT_TRUE(IsImmersiveActive());
app_window_->Minimize();
EXPECT_FALSE(IsImmersiveActive());
......
......@@ -27,7 +27,6 @@
#include "ui/base/ui_base_features.h"
#include "ui/views/mus/desktop_window_tree_host_mus.h"
#include "ui/views/mus/mus_client.h"
#include "ui/views/mus/window_manager_frame_values.h"
BrowserFrameMash::BrowserFrameMash(BrowserFrame* browser_frame,
BrowserView* browser_view)
......
......@@ -54,9 +54,6 @@
-ImmersiveModeBrowserViewTest.TestCaptionButtonsReceiveEventsInAppImmersiveMode*
-ImmersiveModeBrowserViewTest.TestCaptionButtonsReceiveEventsInBrowserImmersiveMode*
# Null immersive_fullscreen_controller_.
-ChromeNativeAppWindowViewsAuraAshBrowserTest.*
# ash::Shell access from ChromeViewsDelegate::CreateDefaultNonClientFrameView()
# e.g. from chromeos::CaptivePortalWindowProxy::Show().
# See https://crbug.com/838974
......
......@@ -43,7 +43,7 @@ namespace views {
namespace {
// As the window manager renderers the non-client decorations this class does
// very little but honor the client area insets from the window manager.
// very little but honor kTopViewInset.
class ClientSideNonClientFrameView : public NonClientFrameView,
public aura::WindowObserver {
public:
......@@ -58,11 +58,11 @@ class ClientSideNonClientFrameView : public NonClientFrameView,
~ClientSideNonClientFrameView() override {}
private:
// Returns the default values of client area insets from the window manager.
static gfx::Insets GetDefaultWindowManagerInsets(bool is_maximized) {
const WindowManagerFrameValues& values =
WindowManagerFrameValues::instance();
return is_maximized ? values.maximized_insets : values.normal_insets;
gfx::Insets GetClientInsets() const {
const int top_inset =
widget_->GetNativeWindow()->GetRootWindow()->GetProperty(
aura::client::kTopViewInset);
return gfx::Insets(top_inset, 0, 0, 0);
}
// View:
......@@ -75,7 +75,7 @@ class ClientSideNonClientFrameView : public NonClientFrameView,
gfx::Rect result(GetLocalBounds());
if (widget_->IsFullscreen())
return result;
result.Inset(GetDefaultWindowManagerInsets(widget_->IsMaximized()));
result.Inset(GetClientInsets());
return result;
}
gfx::Rect GetWindowBoundsForClientBounds(
......@@ -83,12 +83,9 @@ class ClientSideNonClientFrameView : public NonClientFrameView,
if (widget_->IsFullscreen())
return client_bounds;
const gfx::Insets insets(
GetDefaultWindowManagerInsets(widget_->IsMaximized()));
return gfx::Rect(client_bounds.x() - insets.left(),
client_bounds.y() - insets.top(),
client_bounds.width() + insets.width(),
client_bounds.height() + insets.height());
gfx::Rect outset_bounds = client_bounds;
outset_bounds.Inset(-GetClientInsets());
return outset_bounds;
}
int NonClientHitTest(const gfx::Point& point) override { return HTNOWHERE; }
void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask) override {
......@@ -136,17 +133,9 @@ class ClientSideNonClientFrameView : public NonClientFrameView,
void OnWindowPropertyChanged(aura::Window* window,
const void* key,
intptr_t old) override {
// Do a re-layout on state changes which affect GetBoundsForClientView().
// The associated bounds change would also cause a re-layout, but there may
// not be a bounds change or it may come from the server before the state is
// updated.
if (key == aura::client::kShowStateKey) {
if (GetBoundsForClientView() != widget_->client_view()->bounds() &&
window->GetProperty(aura::client::kShowStateKey) !=
ui::SHOW_STATE_MINIMIZED) {
InvalidateLayout();
widget_->GetRootView()->Layout();
}
if (key == aura::client::kTopViewInset) {
InvalidateLayout();
widget_->GetRootView()->Layout();
}
}
......
......@@ -24,7 +24,6 @@
#include "ui/views/mus/mus_client.h"
#include "ui/views/mus/mus_client_test_api.h"
#include "ui/views/mus/screen_mus.h"
#include "ui/views/mus/window_manager_frame_values.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
......@@ -240,42 +239,24 @@ TEST_F(DesktopWindowTreeHostMusTest, ActivateBeforeShow) {
->active_focus_client());
}
// Tests that changes to a widget's show state will cause the client area to be
// updated.
TEST_F(DesktopWindowTreeHostMusTest, ServerShowStateChangeUpdatesClientArea) {
WindowManagerFrameValues test_frame_values;
test_frame_values.normal_insets = {3, 0, 0, 0};
test_frame_values.maximized_insets = {7, 0, 0, 0};
WindowManagerFrameValues::SetInstance(test_frame_values);
// Tests that changes to kTopViewInset will cause the client area to be updated.
TEST_F(DesktopWindowTreeHostMusTest, ServerTopInsetChangeUpdatesClientArea) {
std::unique_ptr<Widget> widget(CreateWidget());
widget->Show();
// Simulate state changes from the server.
auto set_widget_state =
[&widget](ui::WindowShowState state) {
widget->GetNativeWindow()->GetRootWindow()->SetProperty(
aura::client::kShowStateKey, state);
};
// A restored window respects normal_insets (the client area is inset from the
// root view).
gfx::Rect expected_restored_bounds = widget->GetRootView()->bounds();
expected_restored_bounds.Inset(test_frame_values.normal_insets);
EXPECT_EQ(expected_restored_bounds, widget->client_view()->bounds());
// A fullscreen window has no insets.
EXPECT_FALSE(widget->IsFullscreen());
set_widget_state(ui::SHOW_STATE_FULLSCREEN);
EXPECT_TRUE(widget->IsFullscreen());
auto set_top_inset = [&widget](int value) {
widget->GetNativeWindow()->GetRootWindow()->SetProperty(
aura::client::kTopViewInset, value);
};
EXPECT_EQ(widget->GetRootView()->bounds(), widget->client_view()->bounds());
// A maximized window respects maximized_insets.
gfx::Rect expected_maximized_bounds = widget->GetRootView()->bounds();
expected_maximized_bounds.Inset(test_frame_values.maximized_insets);
set_widget_state(ui::SHOW_STATE_MAXIMIZED);
EXPECT_FALSE(widget->IsFullscreen());
EXPECT_EQ(expected_maximized_bounds, widget->client_view()->bounds());
set_top_inset(3);
gfx::Rect root_bounds = widget->GetRootView()->bounds();
root_bounds.Inset(gfx::Insets(3, 0, 0, 0));
set_top_inset(0);
EXPECT_EQ(widget->GetRootView()->bounds(), widget->client_view()->bounds());
}
TEST_F(DesktopWindowTreeHostMusTest, CursorClientDuringTearDown) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment