shell_surface_util.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/exo/shell_surface_util.h"
  5. #include <memory>
  6. #include "base/strings/string_number_conversions.h"
  7. #include "base/trace_event/trace_event.h"
  8. #include "build/chromeos_buildflags.h"
  9. #include "components/exo/permission.h"
  10. #include "components/exo/shell_surface_base.h"
  11. #include "components/exo/surface.h"
  12. #include "components/exo/window_properties.h"
  13. #include "components/exo/wm_helper.h"
  14. #include "ui/aura/client/aura_constants.h"
  15. #include "ui/aura/client/capture_client.h"
  16. #include "ui/aura/window.h"
  17. #include "ui/aura/window_delegate.h"
  18. #include "ui/aura/window_targeter.h"
  19. #include "ui/events/base_event_utils.h"
  20. #include "ui/events/event.h"
  21. #include "ui/views/widget/widget.h"
  22. #include "ui/wm/core/window_util.h"
  23. #if BUILDFLAG(IS_CHROMEOS_ASH)
  24. #include "ash/public/cpp/window_properties.h"
  25. #include "ash/wm/desks/desks_controller.h"
  26. #include "ash/wm/desks/desks_util.h"
  27. #include "chromeos/ui/base/window_properties.h"
  28. #include "components/exo/client_controlled_shell_surface.h"
  29. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  30. namespace exo {
  31. namespace {
  32. DEFINE_UI_CLASS_PROPERTY_KEY(Surface*, kRootSurfaceKey, nullptr)
  33. // Startup Id set by the client.
  34. DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kStartupIdKey, nullptr)
  35. #if BUILDFLAG(IS_CHROMEOS_ASH)
  36. // A property key containing the client controlled shell surface.
  37. DEFINE_UI_CLASS_PROPERTY_KEY(ClientControlledShellSurface*,
  38. kClientControlledShellSurface,
  39. nullptr)
  40. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  41. // Returns true if the component for a located event should be taken care of
  42. // by the window system.
  43. bool ShouldHTComponentBlocked(int component) {
  44. switch (component) {
  45. case HTCAPTION:
  46. case HTCLOSE:
  47. case HTMAXBUTTON:
  48. case HTMINBUTTON:
  49. case HTMENU:
  50. case HTSYSMENU:
  51. return true;
  52. default:
  53. return false;
  54. }
  55. }
  56. // Find the lowest targeter in the parent chain.
  57. aura::WindowTargeter* FindTargeter(ui::EventTarget* target) {
  58. do {
  59. ui::EventTargeter* targeter = target->GetEventTargeter();
  60. if (targeter)
  61. return static_cast<aura::WindowTargeter*>(targeter);
  62. target = target->GetParentTarget();
  63. } while (target);
  64. return nullptr;
  65. }
  66. } // namespace
  67. void SetShellApplicationId(ui::PropertyHandler* property_handler,
  68. const absl::optional<std::string>& id) {
  69. TRACE_EVENT1("exo", "SetApplicationId", "application_id", id ? *id : "null");
  70. if (id)
  71. property_handler->SetProperty(kApplicationIdKey, *id);
  72. else
  73. property_handler->ClearProperty(kApplicationIdKey);
  74. }
  75. const std::string* GetShellApplicationId(const aura::Window* property_handler) {
  76. return property_handler->GetProperty(kApplicationIdKey);
  77. }
  78. void SetShellStartupId(ui::PropertyHandler* property_handler,
  79. const absl::optional<std::string>& id) {
  80. TRACE_EVENT1("exo", "SetStartupId", "startup_id", id ? *id : "null");
  81. if (id)
  82. property_handler->SetProperty(kStartupIdKey, *id);
  83. else
  84. property_handler->ClearProperty(kStartupIdKey);
  85. }
  86. const std::string* GetShellStartupId(const aura::Window* window) {
  87. return window->GetProperty(kStartupIdKey);
  88. }
  89. void SetShellUseImmersiveForFullscreen(aura::Window* window, bool value) {
  90. #if BUILDFLAG(IS_CHROMEOS_ASH)
  91. window->SetProperty(chromeos::kImmersiveImpliedByFullscreen, value);
  92. // Ensure the shelf is fully hidden in plain fullscreen, but shown
  93. // (auto-hides based on mouse movement) when in immersive fullscreen.
  94. window->SetProperty(chromeos::kHideShelfWhenFullscreenKey, !value);
  95. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  96. }
  97. #if BUILDFLAG(IS_CHROMEOS_ASH)
  98. void SetShellClientAccessibilityId(aura::Window* window,
  99. const absl::optional<int32_t>& id) {
  100. TRACE_EVENT1("exo", "SetClientAccessibilityId", "id",
  101. id ? base::NumberToString(*id) : "null");
  102. if (id)
  103. window->SetProperty(ash::kClientAccessibilityIdKey, *id);
  104. else
  105. window->ClearProperty(ash::kClientAccessibilityIdKey);
  106. }
  107. const absl::optional<int32_t> GetShellClientAccessibilityId(
  108. aura::Window* window) {
  109. auto id = window->GetProperty(ash::kClientAccessibilityIdKey);
  110. if (id < 0)
  111. return absl::nullopt;
  112. else
  113. return id;
  114. }
  115. void SetShellClientControlledShellSurface(
  116. ui::PropertyHandler* property_handler,
  117. const absl::optional<ClientControlledShellSurface*>& shell_surface) {
  118. if (shell_surface)
  119. property_handler->SetProperty(kClientControlledShellSurface,
  120. shell_surface.value());
  121. else
  122. property_handler->ClearProperty(kClientControlledShellSurface);
  123. }
  124. ClientControlledShellSurface* GetShellClientControlledShellSurface(
  125. ui::PropertyHandler* property_handler) {
  126. return property_handler->GetProperty(kClientControlledShellSurface);
  127. }
  128. int GetWindowDeskStateChanged(const aura::Window* window) {
  129. constexpr int kToggleVisibleOnAllWorkspacesValue = -1;
  130. if (ash::desks_util::IsWindowVisibleOnAllWorkspaces(window))
  131. return kToggleVisibleOnAllWorkspacesValue;
  132. int workspace = window->GetProperty(aura::client::kWindowWorkspaceKey);
  133. // If workspace is unassigned, returns the active desk index.
  134. if (workspace == aura::client::kWindowWorkspaceUnassignedWorkspace)
  135. workspace = ash::DesksController::Get()->GetActiveDeskIndex();
  136. return workspace;
  137. }
  138. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  139. void SetShellRootSurface(ui::PropertyHandler* property_handler,
  140. Surface* surface) {
  141. property_handler->SetProperty(kRootSurfaceKey, surface);
  142. }
  143. Surface* GetShellRootSurface(const aura::Window* window) {
  144. return window->GetProperty(kRootSurfaceKey);
  145. }
  146. ShellSurfaceBase* GetShellSurfaceBaseForWindow(aura::Window* window) {
  147. // Only windows with a surface can have a shell surface.
  148. if (!GetShellRootSurface(window))
  149. return nullptr;
  150. views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
  151. if (!widget)
  152. return nullptr;
  153. ShellSurfaceBase* shell_surface_base =
  154. static_cast<ShellSurfaceBase*>(widget->widget_delegate());
  155. // We can obtain widget from native window, but not |shell_surface_base|.
  156. // This means we are in the process of destroying this surface so we should
  157. // return nullptr.
  158. if (!shell_surface_base || !shell_surface_base->GetWidget())
  159. return nullptr;
  160. return shell_surface_base;
  161. }
  162. Surface* GetTargetSurfaceForLocatedEvent(
  163. const ui::LocatedEvent* original_event) {
  164. aura::Window* window =
  165. WMHelper::GetInstance()->GetCaptureClient()->GetCaptureWindow();
  166. if (!window) {
  167. return Surface::AsSurface(
  168. static_cast<aura::Window*>(original_event->target()));
  169. }
  170. Surface* root_surface = GetShellRootSurface(window);
  171. // Skip if the event is captured by non exo windows.
  172. if (!root_surface) {
  173. auto* widget = views::Widget::GetTopLevelWidgetForNativeView(window);
  174. if (!widget)
  175. return nullptr;
  176. root_surface = GetShellRootSurface(widget->GetNativeWindow());
  177. if (!root_surface)
  178. return nullptr;
  179. ShellSurfaceBase* shell_surface_base =
  180. GetShellSurfaceBaseForWindow(widget->GetNativeWindow());
  181. // Check if it's overlay window.
  182. if (!shell_surface_base->host_window()->Contains(window) &&
  183. shell_surface_base->GetWidget()->GetNativeWindow() != window) {
  184. return nullptr;
  185. }
  186. }
  187. // Create a clone of the event as targeter may update it during the
  188. // search.
  189. auto cloned = original_event->Clone();
  190. ui::LocatedEvent* event = cloned->AsLocatedEvent();
  191. while (true) {
  192. gfx::PointF location_in_target_f = event->location_f();
  193. gfx::Point location_in_target = event->location();
  194. ui::EventTarget* event_target = window;
  195. aura::WindowTargeter* targeter = FindTargeter(event_target);
  196. DCHECK(targeter);
  197. aura::Window* focused =
  198. static_cast<aura::Window*>(targeter->FindTargetForEvent(window, event));
  199. if (focused) {
  200. Surface* surface = Surface::AsSurface(focused);
  201. if (focused != window)
  202. return surface;
  203. else if (surface && surface->HitTest(location_in_target)) {
  204. // If the targeting fallback to the root (first) window, test the
  205. // hit region again.
  206. return surface;
  207. }
  208. }
  209. // If the event falls into the place where the window system should care
  210. // about (i.e. window caption), do not check the transient parent but just
  211. // return nullptr. See b/149517682.
  212. if (window->delegate() &&
  213. ShouldHTComponentBlocked(
  214. window->delegate()->GetNonClientComponent(location_in_target))) {
  215. return nullptr;
  216. }
  217. aura::Window* parent_window = wm::GetTransientParent(window);
  218. if (!parent_window)
  219. return root_surface;
  220. event->set_location_f(location_in_target_f);
  221. event_target->ConvertEventToTarget(parent_window, event);
  222. window = parent_window;
  223. }
  224. }
  225. Surface* GetTargetSurfaceForKeyboardFocus(aura::Window* window) {
  226. if (!window)
  227. return nullptr;
  228. // The keyboard focus should be set to the root surface.
  229. ShellSurfaceBase* shell_surface_base = nullptr;
  230. for (auto* current = window; current && !shell_surface_base;
  231. current = current->parent()) {
  232. shell_surface_base = GetShellSurfaceBaseForWindow(current);
  233. }
  234. // Make sure the |window| is the toplevel or a host window, but not
  235. // another window added to the toplevel.
  236. if (shell_surface_base && !shell_surface_base->HasOverlay() &&
  237. (shell_surface_base->GetWidget()->GetNativeWindow() == window ||
  238. shell_surface_base->host_window()->Contains(window))) {
  239. return shell_surface_base->root_surface();
  240. }
  241. // Fallback to the window's surface if any. This is used for
  242. // notifications.
  243. return Surface::AsSurface(window);
  244. }
  245. void GrantPermissionToActivate(aura::Window* window, base::TimeDelta timeout) {
  246. // Activation is the only permission, so just set the property. The window
  247. // owns the Permission object.
  248. window->SetProperty(
  249. kPermissionKey,
  250. new Permission(Permission::Capability::kActivate, timeout));
  251. }
  252. void GrantPermissionToActivateIndefinitely(aura::Window* window) {
  253. // Activation is the only permission, so just set the property. The window
  254. // owns the Permission object.
  255. window->SetProperty(kPermissionKey,
  256. new Permission(Permission::Capability::kActivate));
  257. }
  258. void RevokePermissionToActivate(aura::Window* window) {
  259. // Activation is the only permission, so just clear the property.
  260. window->ClearProperty(kPermissionKey);
  261. }
  262. bool HasPermissionToActivate(aura::Window* window) {
  263. Permission* permission = window->GetProperty(kPermissionKey);
  264. return permission && permission->Check(Permission::Capability::kActivate);
  265. }
  266. bool ConsumedByIme(aura::Window* window, const ui::KeyEvent& event) {
  267. // Check if IME consumed the event, to avoid it to be doubly processed.
  268. // First let us see whether IME is active and is in text input mode.
  269. views::Widget* widget = views::Widget::GetTopLevelWidgetForNativeView(window);
  270. ui::InputMethod* ime = widget ? widget->GetInputMethod() : nullptr;
  271. if (!ime || ime->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE ||
  272. ime->GetTextInputType() == ui::TEXT_INPUT_TYPE_NULL) {
  273. return false;
  274. }
  275. // Case 1:
  276. // When IME ate a key event but did not emit character insertion event yet
  277. // (e.g., when it is still showing a candidate list UI to the user,) the
  278. // consumed key event is re-sent after masked |key_code| by VKEY_PROCESSKEY.
  279. if (event.key_code() == ui::VKEY_PROCESSKEY)
  280. return true;
  281. // Except for PROCESSKEY, never discard "key-up" events. A keydown not paired
  282. // by a keyup can trigger a never-ending key repeat in the client, which can
  283. // never be desirable.
  284. if (event.type() == ui::ET_KEY_RELEASED)
  285. return false;
  286. // Case 2:
  287. // When IME ate a key event and generated a single character input, it leaves
  288. // the key event as-is, and in addition calls the active ui::TextInputClient's
  289. // InsertChar() method. (In our case, arc::ArcImeService::InsertChar()).
  290. //
  291. // In Chrome OS (and Web) convention, the two calls won't cause duplicates,
  292. // because key-down events do not mean any character inputs there.
  293. // (InsertChar issues a DOM "keypress" event, which is distinct from keydown.)
  294. // Unfortunately, this is not necessary the case for our clients that may
  295. // treat keydown as a trigger of text inputs. We need suppression for keydown.
  296. //
  297. // Same condition as ash/components/arc/ime/arc_ime_service.cc#InsertChar.
  298. const char16_t ch = event.GetCharacter();
  299. const bool is_control_char =
  300. (0x00 <= ch && ch <= 0x1f) || (0x7f <= ch && ch <= 0x9f);
  301. if (!is_control_char && !ui::IsSystemKeyModifier(event.flags()))
  302. return true;
  303. // Case 3:
  304. // Workaround for apps that doesn't handle hardware keyboard events well.
  305. // Keys typically on software keyboard and lack of them are fatal, namely,
  306. // unmodified enter and backspace keys, are sent through IME.
  307. constexpr int kModifierMask = ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
  308. ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN |
  309. ui::EF_ALTGR_DOWN | ui::EF_MOD3_DOWN;
  310. // Same condition as ash/components/arc/ime/arc_ime_service.cc#InsertChar.
  311. if ((event.flags() & kModifierMask) == 0) {
  312. if (event.key_code() == ui::VKEY_RETURN ||
  313. event.key_code() == ui::VKEY_BACK) {
  314. return true;
  315. }
  316. }
  317. return false;
  318. }
  319. void SetSkipImeProcessingToDescendentSurfaces(aura::Window* window,
  320. bool value) {
  321. if (Surface::AsSurface(window))
  322. window->SetProperty(aura::client::kSkipImeProcessing, value);
  323. for (aura::Window* child : window->children())
  324. SetSkipImeProcessingToDescendentSurfaces(child, value);
  325. }
  326. } // namespace exo