Code: bool ClientControlledShellSurface::OnPreWidgetCommit() {
  if (!widget_) {
    if (!pending_geometry_.IsEmpty())
      origin_ = pending_geometry_.origin();
    CreateShellSurfaceWidget(ash::ToWindowShowState(pending_window_state_));
  }

  ash::wm::WindowState* window_state = GetWindowState();
  if (window_state->GetStateType() == pending_window_state_) {
    if (window_state->IsPip() && !window_state->is_dragged()) {
      client_controlled_state_->set_next_bounds_change_animation_type(
          ash::wm::ClientControlledState::kAnimationAnimated);
    }

    return true;
  }

  if (IsPinned(window_state)) {
    VLOG(1) << "State change was requested while pinned";
    return true;
  }

  auto animation_type = ash::wm::ClientControlledState::kAnimationNone;
  switch (pending_window_state_) {
    case ash::WindowStateType::kNormal:
      if (widget_->IsMaximized() || widget_->IsFullscreen()) {
        animation_type = ash::wm::ClientControlledState::kAnimationCrossFade;
      }
      break;

    case ash::WindowStateType::kMaximized:
    case ash::WindowStateType::kFullscreen:
      if (!window_state->IsPip())
        animation_type = ash::wm::ClientControlledState::kAnimationCrossFade;
      break;

    default:
      break;
  }

  if (pending_window_state_ == ash::WindowStateType::kPip) {
    if (ash::features::IsPipRoundedCornersEnabled()) {
      decorator_ = std::make_unique<ash::RoundedCornerDecorator>(
          window_state->window(), host_window(), host_window()->layer(),
          ash::kPipRoundedCornerRadius);
    }
  } else {
    decorator_.reset();  // Remove rounded corners.
  }
 
   bool wasPip = window_state->IsPip();
 
   if (client_controlled_state_->EnterNextState(window_state,
                                                pending_window_state_)) {
     client_controlled_state_->set_next_bounds_change_animation_type(
        animation_type);
  }

  if (wasPip && !window_state->IsMinimized()) {
    ash::Shell::Get()->split_view_controller()->EndSplitView(
        ash::SplitViewController::EndReason::kPipExpanded);
    window_state->Activate();
  }

  return true;
}

Commit Message: Ignore updatePipBounds before initial bounds is set

When PIP enter/exit transition happens, window state change and
initial bounds change are committed in the same commit. However,
as state change is applied first in OnPreWidgetCommit and the
bounds is update later, if updatePipBounds is called between the
gap, it ends up returning a wrong bounds based on the previous
bounds.

Currently, there are two callstacks that end up triggering
updatePipBounds between the gap: (i) The state change causes
OnWindowAddedToLayout and updatePipBounds is called in OnWMEvent,
(ii) updatePipBounds is called in UpdatePipState to prevent it
from being placed under some system ui.

As it doesn't make sense to call updatePipBounds before the first
bounds is not set, this CL adds a boolean to defer updatePipBounds.

position.

Bug: b130782006
Test: Got VLC into PIP and confirmed it was placed at the correct
Change-Id: I5b9f3644bfb2533fd3f905bc09d49708a5d08a90
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1578719
Commit-Queue: Kazuki Takise <takise@chromium.org>
Auto-Submit: Kazuki Takise <takise@chromium.org>
Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#668724}
CWE ID: CWE-787
bool ClientControlledShellSurface::OnPreWidgetCommit() {
  if (!widget_) {
    if (!pending_geometry_.IsEmpty())
      origin_ = pending_geometry_.origin();
    CreateShellSurfaceWidget(ash::ToWindowShowState(pending_window_state_));
  }

  ash::wm::WindowState* window_state = GetWindowState();
  if (window_state->GetStateType() == pending_window_state_) {
    if (window_state->IsPip() && !window_state->is_dragged()) {
      client_controlled_state_->set_next_bounds_change_animation_type(
          ash::wm::ClientControlledState::kAnimationAnimated);
    }

    return true;
  }

  if (IsPinned(window_state)) {
    VLOG(1) << "State change was requested while pinned";
    return true;
  }

  auto animation_type = ash::wm::ClientControlledState::kAnimationNone;
  switch (pending_window_state_) {
    case ash::WindowStateType::kNormal:
      if (widget_->IsMaximized() || widget_->IsFullscreen()) {
        animation_type = ash::wm::ClientControlledState::kAnimationCrossFade;
      }
      break;

    case ash::WindowStateType::kMaximized:
    case ash::WindowStateType::kFullscreen:
      if (!window_state->IsPip())
        animation_type = ash::wm::ClientControlledState::kAnimationCrossFade;
      break;

    default:
      break;
  }

  if (pending_window_state_ == ash::WindowStateType::kPip) {
    if (ash::features::IsPipRoundedCornersEnabled()) {
      decorator_ = std::make_unique<ash::RoundedCornerDecorator>(
          window_state->window(), host_window(), host_window()->layer(),
          ash::kPipRoundedCornerRadius);
    }
  } else {
    decorator_.reset();  // Remove rounded corners.
  }
 
   bool wasPip = window_state->IsPip();
 
  // As the bounds of the widget is updated later, ensure that no bounds change
  // happens with this state change (e.g. updatePipBounds can be triggered).
  base::AutoReset<bool> resetter(&ignore_bounds_change_request_, true);
   if (client_controlled_state_->EnterNextState(window_state,
                                                pending_window_state_)) {
     client_controlled_state_->set_next_bounds_change_animation_type(
        animation_type);
  }

  if (wasPip && !window_state->IsMinimized()) {
    ash::Shell::Get()->split_view_controller()->EndSplitView(
        ash::SplitViewController::EndReason::kPipExpanded);
    window_state->Activate();
  }

  return true;
}
