/src/serenity/Userland/Libraries/LibWeb/Internals/Internals.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2023, Andreas Kling <kling@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibJS/Runtime/VM.h> |
8 | | #include <LibWeb/Bindings/InternalsPrototype.h> |
9 | | #include <LibWeb/Bindings/Intrinsics.h> |
10 | | #include <LibWeb/DOM/Document.h> |
11 | | #include <LibWeb/DOM/Event.h> |
12 | | #include <LibWeb/DOM/EventTarget.h> |
13 | | #include <LibWeb/DOMURL/DOMURL.h> |
14 | | #include <LibWeb/HTML/BrowsingContext.h> |
15 | | #include <LibWeb/HTML/HTMLElement.h> |
16 | | #include <LibWeb/HTML/Window.h> |
17 | | #include <LibWeb/Internals/Internals.h> |
18 | | #include <LibWeb/Page/InputEvent.h> |
19 | | #include <LibWeb/Page/Page.h> |
20 | | #include <LibWeb/Painting/PaintableBox.h> |
21 | | #include <LibWeb/Painting/ViewportPaintable.h> |
22 | | |
23 | | namespace Web::Internals { |
24 | | |
25 | | JS_DEFINE_ALLOCATOR(Internals); |
26 | | |
27 | | Internals::Internals(JS::Realm& realm) |
28 | 0 | : Bindings::PlatformObject(realm) |
29 | 0 | { |
30 | 0 | } |
31 | | |
32 | 0 | Internals::~Internals() = default; |
33 | | |
34 | | void Internals::initialize(JS::Realm& realm) |
35 | 0 | { |
36 | 0 | Base::initialize(realm); |
37 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE(Internals); |
38 | 0 | } |
39 | | |
40 | | HTML::Window& Internals::internals_window() const |
41 | 0 | { |
42 | 0 | return verify_cast<HTML::Window>(HTML::relevant_global_object(*this)); |
43 | 0 | } |
44 | | |
45 | | Page& Internals::internals_page() const |
46 | 0 | { |
47 | 0 | return internals_window().page(); |
48 | 0 | } |
49 | | |
50 | | void Internals::signal_text_test_is_done(String const& text) |
51 | 0 | { |
52 | 0 | internals_page().client().page_did_finish_text_test(text); |
53 | 0 | } |
54 | | |
55 | | void Internals::gc() |
56 | 0 | { |
57 | 0 | vm().heap().collect_garbage(); |
58 | 0 | } |
59 | | |
60 | | JS::Object* Internals::hit_test(double x, double y) |
61 | 0 | { |
62 | 0 | auto& active_document = internals_window().associated_document(); |
63 | | // NOTE: Force a layout update just before hit testing. This is because the current layout tree, which is required |
64 | | // for stacking context traversal, might not exist if this call occurs between the tear_down_layout_tree() |
65 | | // and update_layout() calls |
66 | 0 | active_document.update_layout(); |
67 | 0 | auto result = active_document.paintable_box()->hit_test({ x, y }, Painting::HitTestType::Exact); |
68 | 0 | if (result.has_value()) { |
69 | 0 | auto hit_tеsting_result = JS::Object::create(realm(), nullptr); |
70 | 0 | hit_tеsting_result->define_direct_property("node", result->dom_node(), JS::default_attributes); |
71 | 0 | hit_tеsting_result->define_direct_property("indexInNode", JS::Value(result->index_in_node), JS::default_attributes); |
72 | 0 | return hit_tеsting_result; |
73 | 0 | } |
74 | 0 | return nullptr; |
75 | 0 | } |
76 | | |
77 | | void Internals::send_text(HTML::HTMLElement& target, String const& text, WebIDL::UnsignedShort modifiers) |
78 | 0 | { |
79 | 0 | auto& page = internals_page(); |
80 | 0 | target.focus(); |
81 | |
|
82 | 0 | for (auto code_point : text.code_points()) |
83 | 0 | page.handle_keydown(UIEvents::code_point_to_key_code(code_point), modifiers, code_point); |
84 | 0 | } |
85 | | |
86 | | void Internals::send_key(HTML::HTMLElement& target, String const& key_name, WebIDL::UnsignedShort modifiers) |
87 | 0 | { |
88 | 0 | auto key_code = UIEvents::key_code_from_string(key_name); |
89 | 0 | target.focus(); |
90 | |
|
91 | 0 | internals_page().handle_keydown(key_code, modifiers, 0); |
92 | 0 | } |
93 | | |
94 | | void Internals::commit_text() |
95 | 0 | { |
96 | 0 | internals_page().handle_keydown(UIEvents::Key_Return, 0, 0); |
97 | 0 | } |
98 | | |
99 | | void Internals::click(double x, double y) |
100 | 0 | { |
101 | 0 | click(x, y, UIEvents::MouseButton::Primary); |
102 | 0 | } |
103 | | |
104 | | void Internals::middle_click(double x, double y) |
105 | 0 | { |
106 | 0 | click(x, y, UIEvents::MouseButton::Middle); |
107 | 0 | } |
108 | | |
109 | | void Internals::click(double x, double y, UIEvents::MouseButton button) |
110 | 0 | { |
111 | 0 | auto& page = internals_page(); |
112 | |
|
113 | 0 | auto position = page.css_to_device_point({ x, y }); |
114 | 0 | page.handle_mousedown(position, position, button, 0, 0); |
115 | 0 | page.handle_mouseup(position, position, button, 0, 0); |
116 | 0 | } |
117 | | |
118 | | void Internals::move_pointer_to(double x, double y) |
119 | 0 | { |
120 | 0 | auto& page = internals_page(); |
121 | |
|
122 | 0 | auto position = page.css_to_device_point({ x, y }); |
123 | 0 | page.handle_mousemove(position, position, 0, 0); |
124 | 0 | } |
125 | | |
126 | | void Internals::wheel(double x, double y, double delta_x, double delta_y) |
127 | 0 | { |
128 | 0 | auto& page = internals_page(); |
129 | |
|
130 | 0 | auto position = page.css_to_device_point({ x, y }); |
131 | 0 | page.handle_mousewheel(position, position, 0, 0, 0, delta_x, delta_y); |
132 | 0 | } |
133 | | |
134 | | WebIDL::ExceptionOr<bool> Internals::dispatch_user_activated_event(DOM::EventTarget& target, DOM::Event& event) |
135 | 0 | { |
136 | 0 | event.set_is_trusted(true); |
137 | 0 | return target.dispatch_event(event); |
138 | 0 | } |
139 | | |
140 | | void Internals::spoof_current_url(String const& url_string) |
141 | 0 | { |
142 | 0 | auto url = DOMURL::parse(url_string); |
143 | |
|
144 | 0 | VERIFY(url.is_valid()); |
145 | | |
146 | 0 | auto origin = url.origin(); |
147 | |
|
148 | 0 | auto& window = internals_window(); |
149 | 0 | window.associated_document().set_url(url); |
150 | 0 | window.associated_document().set_origin(origin); |
151 | 0 | HTML::relevant_settings_object(window.associated_document()).creation_url = url; |
152 | 0 | } |
153 | | |
154 | | JS::NonnullGCPtr<InternalAnimationTimeline> Internals::create_internal_animation_timeline() |
155 | 0 | { |
156 | 0 | auto& realm = this->realm(); |
157 | 0 | return realm.heap().allocate<InternalAnimationTimeline>(realm, realm); |
158 | 0 | } |
159 | | |
160 | | void Internals::simulate_drag_start(double x, double y, String const& name, String const& contents) |
161 | 0 | { |
162 | 0 | Vector<HTML::SelectedFile> files; |
163 | 0 | files.empend(name.to_byte_string(), MUST(ByteBuffer::copy(contents.bytes()))); |
164 | |
|
165 | 0 | auto& page = internals_page(); |
166 | |
|
167 | 0 | auto position = page.css_to_device_point({ x, y }); |
168 | 0 | page.handle_drag_and_drop_event(DragEvent::Type::DragStart, position, position, UIEvents::MouseButton::Primary, 0, 0, move(files)); |
169 | 0 | } |
170 | | |
171 | | void Internals::simulate_drag_move(double x, double y) |
172 | 0 | { |
173 | 0 | auto& page = internals_page(); |
174 | |
|
175 | 0 | auto position = page.css_to_device_point({ x, y }); |
176 | 0 | page.handle_drag_and_drop_event(DragEvent::Type::DragMove, position, position, UIEvents::MouseButton::Primary, 0, 0, {}); |
177 | 0 | } |
178 | | |
179 | | void Internals::simulate_drop(double x, double y) |
180 | 0 | { |
181 | 0 | auto& page = internals_page(); |
182 | |
|
183 | 0 | auto position = page.css_to_device_point({ x, y }); |
184 | 0 | page.handle_drag_and_drop_event(DragEvent::Type::Drop, position, position, UIEvents::MouseButton::Primary, 0, 0, {}); |
185 | 0 | } |
186 | | |
187 | | } |