123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- // Copyright 2018 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 "components/exo/shell_surface_util.h"
- #include <memory>
- #include "base/strings/string_number_conversions.h"
- #include "base/trace_event/trace_event.h"
- #include "build/chromeos_buildflags.h"
- #include "components/exo/permission.h"
- #include "components/exo/shell_surface_base.h"
- #include "components/exo/surface.h"
- #include "components/exo/window_properties.h"
- #include "components/exo/wm_helper.h"
- #include "ui/aura/client/aura_constants.h"
- #include "ui/aura/client/capture_client.h"
- #include "ui/aura/window.h"
- #include "ui/aura/window_delegate.h"
- #include "ui/aura/window_targeter.h"
- #include "ui/events/base_event_utils.h"
- #include "ui/events/event.h"
- #include "ui/views/widget/widget.h"
- #include "ui/wm/core/window_util.h"
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- #include "ash/public/cpp/window_properties.h"
- #include "ash/wm/desks/desks_controller.h"
- #include "ash/wm/desks/desks_util.h"
- #include "chromeos/ui/base/window_properties.h"
- #include "components/exo/client_controlled_shell_surface.h"
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- namespace exo {
- namespace {
- DEFINE_UI_CLASS_PROPERTY_KEY(Surface*, kRootSurfaceKey, nullptr)
- // Startup Id set by the client.
- DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kStartupIdKey, nullptr)
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- // A property key containing the client controlled shell surface.
- DEFINE_UI_CLASS_PROPERTY_KEY(ClientControlledShellSurface*,
- kClientControlledShellSurface,
- nullptr)
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- // Returns true if the component for a located event should be taken care of
- // by the window system.
- bool ShouldHTComponentBlocked(int component) {
- switch (component) {
- case HTCAPTION:
- case HTCLOSE:
- case HTMAXBUTTON:
- case HTMINBUTTON:
- case HTMENU:
- case HTSYSMENU:
- return true;
- default:
- return false;
- }
- }
- // Find the lowest targeter in the parent chain.
- aura::WindowTargeter* FindTargeter(ui::EventTarget* target) {
- do {
- ui::EventTargeter* targeter = target->GetEventTargeter();
- if (targeter)
- return static_cast<aura::WindowTargeter*>(targeter);
- target = target->GetParentTarget();
- } while (target);
- return nullptr;
- }
- } // namespace
- void SetShellApplicationId(ui::PropertyHandler* property_handler,
- const absl::optional<std::string>& id) {
- TRACE_EVENT1("exo", "SetApplicationId", "application_id", id ? *id : "null");
- if (id)
- property_handler->SetProperty(kApplicationIdKey, *id);
- else
- property_handler->ClearProperty(kApplicationIdKey);
- }
- const std::string* GetShellApplicationId(const aura::Window* property_handler) {
- return property_handler->GetProperty(kApplicationIdKey);
- }
- void SetShellStartupId(ui::PropertyHandler* property_handler,
- const absl::optional<std::string>& id) {
- TRACE_EVENT1("exo", "SetStartupId", "startup_id", id ? *id : "null");
- if (id)
- property_handler->SetProperty(kStartupIdKey, *id);
- else
- property_handler->ClearProperty(kStartupIdKey);
- }
- const std::string* GetShellStartupId(const aura::Window* window) {
- return window->GetProperty(kStartupIdKey);
- }
- void SetShellUseImmersiveForFullscreen(aura::Window* window, bool value) {
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- window->SetProperty(chromeos::kImmersiveImpliedByFullscreen, value);
- // Ensure the shelf is fully hidden in plain fullscreen, but shown
- // (auto-hides based on mouse movement) when in immersive fullscreen.
- window->SetProperty(chromeos::kHideShelfWhenFullscreenKey, !value);
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- }
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- void SetShellClientAccessibilityId(aura::Window* window,
- const absl::optional<int32_t>& id) {
- TRACE_EVENT1("exo", "SetClientAccessibilityId", "id",
- id ? base::NumberToString(*id) : "null");
- if (id)
- window->SetProperty(ash::kClientAccessibilityIdKey, *id);
- else
- window->ClearProperty(ash::kClientAccessibilityIdKey);
- }
- const absl::optional<int32_t> GetShellClientAccessibilityId(
- aura::Window* window) {
- auto id = window->GetProperty(ash::kClientAccessibilityIdKey);
- if (id < 0)
- return absl::nullopt;
- else
- return id;
- }
- void SetShellClientControlledShellSurface(
- ui::PropertyHandler* property_handler,
- const absl::optional<ClientControlledShellSurface*>& shell_surface) {
- if (shell_surface)
- property_handler->SetProperty(kClientControlledShellSurface,
- shell_surface.value());
- else
- property_handler->ClearProperty(kClientControlledShellSurface);
- }
- ClientControlledShellSurface* GetShellClientControlledShellSurface(
- ui::PropertyHandler* property_handler) {
- return property_handler->GetProperty(kClientControlledShellSurface);
- }
- int GetWindowDeskStateChanged(const aura::Window* window) {
- constexpr int kToggleVisibleOnAllWorkspacesValue = -1;
- if (ash::desks_util::IsWindowVisibleOnAllWorkspaces(window))
- return kToggleVisibleOnAllWorkspacesValue;
- int workspace = window->GetProperty(aura::client::kWindowWorkspaceKey);
- // If workspace is unassigned, returns the active desk index.
- if (workspace == aura::client::kWindowWorkspaceUnassignedWorkspace)
- workspace = ash::DesksController::Get()->GetActiveDeskIndex();
- return workspace;
- }
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- void SetShellRootSurface(ui::PropertyHandler* property_handler,
- Surface* surface) {
- property_handler->SetProperty(kRootSurfaceKey, surface);
- }
- Surface* GetShellRootSurface(const aura::Window* window) {
- return window->GetProperty(kRootSurfaceKey);
- }
- ShellSurfaceBase* GetShellSurfaceBaseForWindow(aura::Window* window) {
- // Only windows with a surface can have a shell surface.
- if (!GetShellRootSurface(window))
- return nullptr;
- views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
- if (!widget)
- return nullptr;
- ShellSurfaceBase* shell_surface_base =
- static_cast<ShellSurfaceBase*>(widget->widget_delegate());
- // We can obtain widget from native window, but not |shell_surface_base|.
- // This means we are in the process of destroying this surface so we should
- // return nullptr.
- if (!shell_surface_base || !shell_surface_base->GetWidget())
- return nullptr;
- return shell_surface_base;
- }
- Surface* GetTargetSurfaceForLocatedEvent(
- const ui::LocatedEvent* original_event) {
- aura::Window* window =
- WMHelper::GetInstance()->GetCaptureClient()->GetCaptureWindow();
- if (!window) {
- return Surface::AsSurface(
- static_cast<aura::Window*>(original_event->target()));
- }
- Surface* root_surface = GetShellRootSurface(window);
- // Skip if the event is captured by non exo windows.
- if (!root_surface) {
- auto* widget = views::Widget::GetTopLevelWidgetForNativeView(window);
- if (!widget)
- return nullptr;
- root_surface = GetShellRootSurface(widget->GetNativeWindow());
- if (!root_surface)
- return nullptr;
- ShellSurfaceBase* shell_surface_base =
- GetShellSurfaceBaseForWindow(widget->GetNativeWindow());
- // Check if it's overlay window.
- if (!shell_surface_base->host_window()->Contains(window) &&
- shell_surface_base->GetWidget()->GetNativeWindow() != window) {
- return nullptr;
- }
- }
- // Create a clone of the event as targeter may update it during the
- // search.
- auto cloned = original_event->Clone();
- ui::LocatedEvent* event = cloned->AsLocatedEvent();
- while (true) {
- gfx::PointF location_in_target_f = event->location_f();
- gfx::Point location_in_target = event->location();
- ui::EventTarget* event_target = window;
- aura::WindowTargeter* targeter = FindTargeter(event_target);
- DCHECK(targeter);
- aura::Window* focused =
- static_cast<aura::Window*>(targeter->FindTargetForEvent(window, event));
- if (focused) {
- Surface* surface = Surface::AsSurface(focused);
- if (focused != window)
- return surface;
- else if (surface && surface->HitTest(location_in_target)) {
- // If the targeting fallback to the root (first) window, test the
- // hit region again.
- return surface;
- }
- }
- // If the event falls into the place where the window system should care
- // about (i.e. window caption), do not check the transient parent but just
- // return nullptr. See b/149517682.
- if (window->delegate() &&
- ShouldHTComponentBlocked(
- window->delegate()->GetNonClientComponent(location_in_target))) {
- return nullptr;
- }
- aura::Window* parent_window = wm::GetTransientParent(window);
- if (!parent_window)
- return root_surface;
- event->set_location_f(location_in_target_f);
- event_target->ConvertEventToTarget(parent_window, event);
- window = parent_window;
- }
- }
- Surface* GetTargetSurfaceForKeyboardFocus(aura::Window* window) {
- if (!window)
- return nullptr;
- // The keyboard focus should be set to the root surface.
- ShellSurfaceBase* shell_surface_base = nullptr;
- for (auto* current = window; current && !shell_surface_base;
- current = current->parent()) {
- shell_surface_base = GetShellSurfaceBaseForWindow(current);
- }
- // Make sure the |window| is the toplevel or a host window, but not
- // another window added to the toplevel.
- if (shell_surface_base && !shell_surface_base->HasOverlay() &&
- (shell_surface_base->GetWidget()->GetNativeWindow() == window ||
- shell_surface_base->host_window()->Contains(window))) {
- return shell_surface_base->root_surface();
- }
- // Fallback to the window's surface if any. This is used for
- // notifications.
- return Surface::AsSurface(window);
- }
- void GrantPermissionToActivate(aura::Window* window, base::TimeDelta timeout) {
- // Activation is the only permission, so just set the property. The window
- // owns the Permission object.
- window->SetProperty(
- kPermissionKey,
- new Permission(Permission::Capability::kActivate, timeout));
- }
- void GrantPermissionToActivateIndefinitely(aura::Window* window) {
- // Activation is the only permission, so just set the property. The window
- // owns the Permission object.
- window->SetProperty(kPermissionKey,
- new Permission(Permission::Capability::kActivate));
- }
- void RevokePermissionToActivate(aura::Window* window) {
- // Activation is the only permission, so just clear the property.
- window->ClearProperty(kPermissionKey);
- }
- bool HasPermissionToActivate(aura::Window* window) {
- Permission* permission = window->GetProperty(kPermissionKey);
- return permission && permission->Check(Permission::Capability::kActivate);
- }
- bool ConsumedByIme(aura::Window* window, const ui::KeyEvent& event) {
- // Check if IME consumed the event, to avoid it to be doubly processed.
- // First let us see whether IME is active and is in text input mode.
- views::Widget* widget = views::Widget::GetTopLevelWidgetForNativeView(window);
- ui::InputMethod* ime = widget ? widget->GetInputMethod() : nullptr;
- if (!ime || ime->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE ||
- ime->GetTextInputType() == ui::TEXT_INPUT_TYPE_NULL) {
- return false;
- }
- // Case 1:
- // When IME ate a key event but did not emit character insertion event yet
- // (e.g., when it is still showing a candidate list UI to the user,) the
- // consumed key event is re-sent after masked |key_code| by VKEY_PROCESSKEY.
- if (event.key_code() == ui::VKEY_PROCESSKEY)
- return true;
- // Except for PROCESSKEY, never discard "key-up" events. A keydown not paired
- // by a keyup can trigger a never-ending key repeat in the client, which can
- // never be desirable.
- if (event.type() == ui::ET_KEY_RELEASED)
- return false;
- // Case 2:
- // When IME ate a key event and generated a single character input, it leaves
- // the key event as-is, and in addition calls the active ui::TextInputClient's
- // InsertChar() method. (In our case, arc::ArcImeService::InsertChar()).
- //
- // In Chrome OS (and Web) convention, the two calls won't cause duplicates,
- // because key-down events do not mean any character inputs there.
- // (InsertChar issues a DOM "keypress" event, which is distinct from keydown.)
- // Unfortunately, this is not necessary the case for our clients that may
- // treat keydown as a trigger of text inputs. We need suppression for keydown.
- //
- // Same condition as ash/components/arc/ime/arc_ime_service.cc#InsertChar.
- const char16_t ch = event.GetCharacter();
- const bool is_control_char =
- (0x00 <= ch && ch <= 0x1f) || (0x7f <= ch && ch <= 0x9f);
- if (!is_control_char && !ui::IsSystemKeyModifier(event.flags()))
- return true;
- // Case 3:
- // Workaround for apps that doesn't handle hardware keyboard events well.
- // Keys typically on software keyboard and lack of them are fatal, namely,
- // unmodified enter and backspace keys, are sent through IME.
- constexpr int kModifierMask = ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
- ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN |
- ui::EF_ALTGR_DOWN | ui::EF_MOD3_DOWN;
- // Same condition as ash/components/arc/ime/arc_ime_service.cc#InsertChar.
- if ((event.flags() & kModifierMask) == 0) {
- if (event.key_code() == ui::VKEY_RETURN ||
- event.key_code() == ui::VKEY_BACK) {
- return true;
- }
- }
- return false;
- }
- void SetSkipImeProcessingToDescendentSurfaces(aura::Window* window,
- bool value) {
- if (Surface::AsSurface(window))
- window->SetProperty(aura::client::kSkipImeProcessing, value);
- for (aura::Window* child : window->children())
- SetSkipImeProcessingToDescendentSurfaces(child, value);
- }
- } // namespace exo
|