910e62b5创建于 1月15日历史提交
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/aura/window_tree_host_platform.h"

#include <memory>
#include <optional>
#include <utility>

#include "base/check_is_test.h"
#include "base/functional/bind.h"
#include "base/notimplemented.h"
#include "base/observer_list.h"
#include "base/run_loop.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/env.h"
#include "ui/aura/host_frame_rate_throttler.h"
#include "ui/aura/native_window_occlusion_tracker.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host_observer.h"
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
#include "ui/base/layout.h"
#include "ui/base/view_prop.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/events/keyboard_hook.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/platform_window/platform_window.h"
#include "ui/platform_window/platform_window_init_properties.h"

#if BUILDFLAG(IS_OZONE)
#include "ui/events/keycodes/dom/dom_keyboard_layout_map.h"
#include "ui/events/ozone/events_ozone.h"
#include "ui/ozone/public/ozone_platform.h"
#endif

#if BUILDFLAG(IS_WIN)
#include "ui/platform_window/win/win_window.h"
#endif

namespace aura {

namespace {
WindowTreeHostPlatform::PlatformWindowFactoryDelegateForTesting*
    g_platform_window_factory_delegate_for_testing = nullptr;

const char kWindowTreeHostPlatformForAcceleratedWidget[] =
    "__AURA_WINDOW_TREE_HOST_PLATFORM_ACCELERATED_WIDGET__";
}

// static
std::unique_ptr<WindowTreeHost> WindowTreeHost::Create(
    ui::PlatformWindowInitProperties properties) {
  return std::make_unique<WindowTreeHostPlatform>(
      std::move(properties),
      std::make_unique<aura::Window>(nullptr, client::WINDOW_TYPE_UNKNOWN));
}

WindowTreeHostPlatform::WindowTreeHostPlatform(
    ui::PlatformWindowInitProperties properties,
    std::unique_ptr<Window> window)
    : WindowTreeHost(std::move(window)),
      widget_(gfx::kNullAcceleratedWidget),
      current_cursor_(ui::mojom::CursorType::kNull) {
  size_in_pixels_ = properties.bounds.size();
  CreateCompositor(false, false, properties.enable_compositing_based_throttling,
                   properties.compositor_memory_limit_mb);
  CreateAndSetPlatformWindow(std::move(properties));
}

WindowTreeHostPlatform::WindowTreeHostPlatform(std::unique_ptr<Window> window)
    : WindowTreeHost(std::move(window)),
      widget_(gfx::kNullAcceleratedWidget),
      current_cursor_(ui::mojom::CursorType::kNull) {}

// static
WindowTreeHostPlatform* WindowTreeHostPlatform::GetHostForWindow(
    aura::Window* window) {
  return reinterpret_cast<WindowTreeHostPlatform*>(
      ui::ViewProp::GetValue(window->GetHost()->GetAcceleratedWidget(),
                             kWindowTreeHostPlatformForAcceleratedWidget));
}

void WindowTreeHostPlatform::CreateAndSetPlatformWindow(
    ui::PlatformWindowInitProperties properties) {
  // Cache initial size used to create |platform_window_| so that it does not
  // end up propagating unneeded bounds change event when it is first notified
  // through OnBoundsChanged, which may lead to unneeded re-layouts, etc.
  size_in_pixels_ = properties.bounds.size();
  platform_window_ = CreatePlatformWindow(std::move(properties));
}

void WindowTreeHostPlatform::SetPlatformWindow(
    std::unique_ptr<ui::PlatformWindow> window) {
  platform_window_ = std::move(window);
}

WindowTreeHostPlatform::~WindowTreeHostPlatform() {
  DestroyCompositor();
  DestroyDispatcher();

  // |platform_window_| may not exist yet.
  if (platform_window_)
    platform_window_->Close();
}

ui::EventSource* WindowTreeHostPlatform::GetEventSource() {
  return this;
}

gfx::AcceleratedWidget WindowTreeHostPlatform::GetAcceleratedWidget() {
  return widget_;
}

void WindowTreeHostPlatform::ShowImpl() {
  platform_window_->Show();
}

void WindowTreeHostPlatform::HideImpl() {
  platform_window_->Hide();
}

gfx::Rect WindowTreeHostPlatform::GetBoundsInPixels() const {
  return platform_window_->GetBoundsInPixels();
}

void WindowTreeHostPlatform::SetBoundsInPixels(const gfx::Rect& bounds) {
  platform_window_->SetBoundsInPixels(bounds);
}

void WindowTreeHostPlatform::SetCapture() {
#if BUILDFLAG(IS_OZONE)
  if (ui::IsNativeUiEventDispatchDisabled()) {
    return;
  }
#endif
  platform_window_->SetCapture();
}

void WindowTreeHostPlatform::ReleaseCapture() {
  platform_window_->ReleaseCapture();
}

gfx::Point WindowTreeHostPlatform::GetLocationOnScreenInPixels() const {
  return platform_window_->GetBoundsInPixels().origin();
}

bool WindowTreeHostPlatform::CaptureSystemKeyEventsImpl(
    std::optional<base::flat_set<ui::DomCode>> dom_codes) {
  // Only one KeyboardHook should be active at a time, otherwise there will be
  // problems with event routing (i.e. which Hook takes precedence) and
  // destruction ordering.
  DCHECK(!keyboard_hook_);
  keyboard_hook_ = ui::KeyboardHook::CreateModifierKeyboardHook(
      std::move(dom_codes), GetAcceleratedWidget(),
      base::BindRepeating(
          [](ui::PlatformWindowDelegate* delegate, ui::KeyEvent* event) {
            delegate->DispatchEvent(event);
          },
          base::Unretained(this)));

  return keyboard_hook_ != nullptr;
}

void WindowTreeHostPlatform::ReleaseSystemKeyEventCapture() {
  keyboard_hook_.reset();
}

bool WindowTreeHostPlatform::IsKeyLocked(ui::DomCode dom_code) {
  return keyboard_hook_ && keyboard_hook_->IsKeyLocked(dom_code);
}

base::flat_map<std::string, std::string>
WindowTreeHostPlatform::GetKeyboardLayoutMap() {
#if BUILDFLAG(IS_OZONE)
  return ui::GenerateDomKeyboardLayoutMap();
#else
  NOTIMPLEMENTED();
  return {};
#endif
}

void WindowTreeHostPlatform::OnVideoCaptureLockCreated() {
  if (platform_window_) {
    platform_window_->SetVideoCapture();
  }
}

void WindowTreeHostPlatform::OnVideoCaptureLockDestroyed() {
  if (platform_window_) {
    platform_window_->ReleaseVideoCapture();
  }
}

void WindowTreeHostPlatform::SetCursorNative(gfx::NativeCursor cursor) {
  if (cursor == current_cursor_)
    return;
  current_cursor_ = cursor;

  platform_window_->SetCursor(cursor.platform());
}

void WindowTreeHostPlatform::MoveCursorToScreenLocationInPixels(
    const gfx::Point& location_in_pixels) {
#if BUILDFLAG(IS_OZONE)
  if (ui::IsNativeUiEventDispatchDisabled()) {
    // Unit tests should not test or rely on the native cursor position because
    // it is shared between multiple tests.
    return;
  }
#endif
  platform_window_->MoveCursorTo(location_in_pixels);
}

void WindowTreeHostPlatform::OnCursorVisibilityChangedNative(bool show) {
  NOTIMPLEMENTED_LOG_ONCE();
}

void WindowTreeHostPlatform::LockMouse(Window* window) {
  window->SetCapture();
  WindowTreeHost::LockMouse(window);
}

std::unique_ptr<ui::PlatformWindow>
WindowTreeHostPlatform::CreatePlatformWindow(
    ui::PlatformWindowInitProperties properties) {
  if (g_platform_window_factory_delegate_for_testing) {
    return g_platform_window_factory_delegate_for_testing->Create(this);
  }
#if BUILDFLAG(IS_OZONE)
  return ui::OzonePlatform::GetInstance()->CreatePlatformWindow(
      this, std::move(properties));
#elif BUILDFLAG(IS_WIN)
  return std::make_unique<ui::WinWindow>(this, properties.bounds);
#else
  NOTIMPLEMENTED();
  return nullptr;
#endif
}

// static
void WindowTreeHostPlatform::SetPlatformWindowFactoryDelegateForTesting(
    PlatformWindowFactoryDelegateForTesting* delegate) {
  CHECK_IS_TEST();
  g_platform_window_factory_delegate_for_testing = delegate;
}

void WindowTreeHostPlatform::OnBoundsChanged(const BoundsChange& change) {
  // It's possible this function may be called recursively. Only notify
  // observers on initial entry. This way observers can safely assume that
  // OnHostDidProcessBoundsChange() is called when all bounds changes have
  // completed.
  if (++on_bounds_changed_recursion_depth_ == 1) {
    observers().Notify(&WindowTreeHostObserver::OnHostWillProcessBoundsChange,
                       this);
  }

  const auto preferred_scale =
      display::Screen::Get()->GetPreferredScaleFactorForWindow(window());
  float current_scale = compositor()->device_scale_factor();
  float new_scale = preferred_scale.value_or(1.0f);
  auto weak_ref = GetWeakPtr();
  auto new_size = GetBoundsInPixels().size();
  bool size_changed = size_in_pixels_ != new_size;
  size_in_pixels_ = new_size;
  if (change.origin_changed) {
    OnHostMovedInPixels();
    // Changing the bounds may destroy this.
    if (!weak_ref)
      return;
  }
  if (size_changed || current_scale != new_scale) {
    OnHostResizedInPixels(new_size);
    // Changing the size may destroy this.
    if (!weak_ref)
      return;
  }
  DCHECK_GT(on_bounds_changed_recursion_depth_, 0);
  if (--on_bounds_changed_recursion_depth_ == 0) {
    observers().Notify(&WindowTreeHostObserver::OnHostDidProcessBoundsChange,
                       this);
  }
}

void WindowTreeHostPlatform::OnDamageRect(const gfx::Rect& damage_rect) {
  compositor()->ScheduleRedrawRect(damage_rect);
}

void WindowTreeHostPlatform::DispatchEvent(ui::Event* event) {
  TRACE_EVENT0("input", "WindowTreeHostPlatform::DispatchEvent");
  ui::EventDispatchDetails details = SendEventToSink(event);
  if (details.dispatcher_destroyed)
    event->SetHandled();
}

void WindowTreeHostPlatform::OnCloseRequest() {
  OnHostCloseRequested();
}

void WindowTreeHostPlatform::OnClosed() {}

void WindowTreeHostPlatform::OnWindowStateChanged(
    ui::PlatformWindowState old_state,
    ui::PlatformWindowState new_state) {}

void WindowTreeHostPlatform::OnLostCapture() {
  OnHostLostWindowCapture();
}

void WindowTreeHostPlatform::OnAcceleratedWidgetAvailable(
    gfx::AcceleratedWidget widget) {
  prop_ = std::make_unique<ui::ViewProp>(
      widget, kWindowTreeHostPlatformForAcceleratedWidget, this);
  widget_ = widget;
  // This may be called before the Compositor has been created.
  if (compositor())
    WindowTreeHost::OnAcceleratedWidgetAvailable();
}

void WindowTreeHostPlatform::OnWillDestroyAcceleratedWidget() {}

void WindowTreeHostPlatform::OnAcceleratedWidgetDestroyed() {
  gfx::AcceleratedWidget widget = compositor()->ReleaseAcceleratedWidget();
  DCHECK_EQ(widget, widget_);
  widget_ = gfx::kNullAcceleratedWidget;
}

void WindowTreeHostPlatform::OnActivationChanged(bool active) {}

void WindowTreeHostPlatform::OnCursorUpdate() {
  client::CursorClient* cursor_client = client::GetCursorClient(window());
  if (cursor_client) {
    auto display = display::Screen::Get()->GetDisplayNearestWindow(window());
    DCHECK(display.is_valid());
    cursor_client->SetDisplay(display);
  }
}

void WindowTreeHostPlatform::OnOcclusionStateChanged(
    ui::PlatformWindowOcclusionState occlusion_state) {
  auto aura_occlusion_state = Window::OcclusionState::UNKNOWN;
  switch (occlusion_state) {
    case ui::PlatformWindowOcclusionState::kUnknown:
      aura_occlusion_state = Window::OcclusionState::UNKNOWN;
      break;
    case ui::PlatformWindowOcclusionState::kVisible:
      aura_occlusion_state = Window::OcclusionState::VISIBLE;
      break;
    case ui::PlatformWindowOcclusionState::kOccluded:
      aura_occlusion_state = Window::OcclusionState::OCCLUDED;
      break;
    case ui::PlatformWindowOcclusionState::kHidden:
      aura_occlusion_state = Window::OcclusionState::HIDDEN;
      break;
  }
  SetNativeWindowOcclusionState(aura_occlusion_state, {});
}

int64_t WindowTreeHostPlatform::OnStateUpdate(
    const PlatformWindowDelegate::State& old,
    const PlatformWindowDelegate::State& latest) {
  if (old.window_state != latest.window_state) {
    OnWindowStateChanged(old.window_state, latest.window_state);
  }

  if (old.bounds_dip != latest.bounds_dip || old.size_px != latest.size_px ||
      old.window_scale != latest.window_scale) {
    bool origin_changed = old.bounds_dip.origin() != latest.bounds_dip.origin();
    OnBoundsChanged({origin_changed});
  }

  bool needs_frame = latest.WillProduceFrameOnUpdateFrom(old);
  if (old.occlusion_state != latest.occlusion_state &&
      NativeWindowOcclusionTracker::
          IsNativeWindowOcclusionTrackingAlwaysEnabled(this)) {
    const bool visible_before = compositor()->IsVisible();
    OnOcclusionStateChanged(latest.occlusion_state);
    if (!visible_before && compositor()->IsVisible()) {
      // If the compositor has become visible, make sure to wait for a frame.
      needs_frame = true;
    }
  }

  // Only set the sequence ID if this change will produce a frame.
  // If it won't, we may wait indefinitely for a frame that will never come.
  // If the compositor is not visible, we will not get a frame, so don't wait.
  if (!needs_frame || !compositor()->IsVisible()) {
    return -1;
  }

  // Update window()'s LocalSurfaceId. This will ensure that the parent ID is
  // updated both here and for LayerTreeHostImpl. So, the CompositorFrame sent
  // by LayerTreeHostImpl will include the updated parent ID for
  // synchronization. Some operations may have already updated the
  // LocalSurfaceId, but this only modifies pending commit state, so it's not
  // expensive.
  window()->AllocateLocalSurfaceId();
  compositor()->SetLocalSurfaceIdFromParent(window()->GetLocalSurfaceId());

  return window()->GetLocalSurfaceId().parent_sequence_number();
}

void WindowTreeHostPlatform::OnDisplayColorSpacesChanged(
    scoped_refptr<gfx::DisplayColorSpacesRef> color_spaces) {
  WindowTreeHost::OnDisplayColorSpacesChanged(std::move(color_spaces));
}

}  // namespace aura