CloseWatcherManager.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /*
  2. * Copyright (c) 2024, the Ladybird developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/TypeCasts.h>
  7. #include <LibWeb/DOM/Document.h>
  8. #include <LibWeb/DOM/EventDispatcher.h>
  9. #include <LibWeb/DOM/IDLEventListener.h>
  10. #include <LibWeb/HTML/CloseWatcher.h>
  11. #include <LibWeb/HTML/CloseWatcherManager.h>
  12. #include <LibWeb/HTML/Window.h>
  13. namespace Web::HTML {
  14. GC_DEFINE_ALLOCATOR(CloseWatcherManager);
  15. GC::Ref<CloseWatcherManager> CloseWatcherManager::create(JS::Realm& realm)
  16. {
  17. return realm.create<CloseWatcherManager>(realm);
  18. }
  19. CloseWatcherManager::CloseWatcherManager(JS::Realm& realm)
  20. : PlatformObject(realm)
  21. {
  22. }
  23. void CloseWatcherManager::add(GC::Ref<CloseWatcher> close_watcher)
  24. {
  25. // If manager's groups's size is less than manager's allowed number of groups
  26. if (m_groups.size() < m_allowed_number_of_groups) {
  27. // then append « closeWatcher » to manager's groups.
  28. GC::RootVector<GC::Ref<CloseWatcher>> new_group(realm().heap());
  29. new_group.append(close_watcher);
  30. m_groups.append(move(new_group));
  31. } else {
  32. // Assert: manager's groups's size is at least 1 in this branch, since manager's allowed number of groups is always at least 1.
  33. VERIFY(!m_groups.is_empty());
  34. // Append closeWatcher to manager's groups's last item.
  35. m_groups.last().append(close_watcher);
  36. }
  37. // Set manager's next user interaction allows a new group to true.
  38. m_next_user_interaction_allows_a_new_group = true;
  39. }
  40. void CloseWatcherManager::remove(CloseWatcher const& close_watcher)
  41. {
  42. // 2. For each group of manager's groups: remove closeWatcher from group
  43. for (auto& group : m_groups) {
  44. group.remove_first_matching([&close_watcher](GC::Ref<CloseWatcher>& entry) {
  45. return entry.ptr() == &close_watcher;
  46. });
  47. }
  48. // 3. Remove any item from manager's group that is empty
  49. m_groups.remove_all_matching([](auto& group) { return group.is_empty(); });
  50. }
  51. // https://html.spec.whatwg.org/multipage/interaction.html#process-close-watchers
  52. bool CloseWatcherManager::process_close_watchers()
  53. {
  54. // 1. Let processedACloseWatcher be false.
  55. bool processed_a_close_watcher = false;
  56. // 2. If window's close watcher manager's groups is not empty:
  57. if (!m_groups.is_empty()) {
  58. // 2.1 Let group be the last item in window's close watcher manager's groups.
  59. auto& group = m_groups.last();
  60. // Ambiguous spec wording. We copy the groups to avoid modifying the original while iterating.
  61. // See https://github.com/whatwg/html/issues/10240
  62. GC::RootVector<GC::Ref<CloseWatcher>> group_copy(realm().heap());
  63. group_copy.ensure_capacity(group.size());
  64. for (auto& close_watcher : group) {
  65. group_copy.append(close_watcher);
  66. }
  67. // 2.2 For each closeWatcher of group, in reverse order:
  68. for (auto it = group_copy.rbegin(); it != group_copy.rend(); ++it) {
  69. // 2.1.1 Set processedACloseWatcher to true.
  70. processed_a_close_watcher = true;
  71. // 2.1.2 Let shouldProceed be the result of requesting to close closeWatcher.
  72. bool should_proceed = (*it)->request_close();
  73. // 2.1.3 If shouldProceed is false, then break;
  74. if (!should_proceed)
  75. break;
  76. }
  77. }
  78. // 3. If window's close watcher manager's allowed number of groups is greater than 1, decrement it by 1.
  79. if (m_allowed_number_of_groups > 1)
  80. m_allowed_number_of_groups--;
  81. // 4. Return processedACloseWatcher.
  82. return processed_a_close_watcher;
  83. }
  84. // https://html.spec.whatwg.org/multipage/interaction.html#notify-the-close-watcher-manager-about-user-activation
  85. void CloseWatcherManager::notify_about_user_activation()
  86. {
  87. // 1. Let manager be window's close watcher manager.
  88. // 2. If manager's next user interaction allows a new group is true, then increment manager's allowed number of groups.
  89. if (m_next_user_interaction_allows_a_new_group)
  90. m_allowed_number_of_groups++;
  91. // 3. Set manager's next user interaction allows a new group to false.
  92. m_next_user_interaction_allows_a_new_group = false;
  93. }
  94. // https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-request-close
  95. bool CloseWatcherManager::can_prevent_close()
  96. {
  97. // 5. Let canPreventClose be true if window's close watcher manager's groups's size is less than window's close watcher manager's allowed number of groups...
  98. return m_groups.size() < m_allowed_number_of_groups;
  99. }
  100. void CloseWatcherManager::visit_edges(JS::Cell::Visitor& visitor)
  101. {
  102. Base::visit_edges(visitor);
  103. visitor.visit(m_groups);
  104. }
  105. }