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