ladybird/Userland/Libraries/LibWeb/Internals/Internals.cpp
Timothy Flynn e8a1b89447 LibWeb: Begin implementing the drag-and-drop processing model
The drag-and-drop processing model allows for users to drag around
either elements within the DOM or objects completely outside the DOM.
This drag event can either end without action (via cancellation or user
input), or in a drop event, where the dragged object is dropped onto
another element within the DOM.

The processing model is rather large. This implements enough of it to
allow the UI process to specifically handle dragging objects outside of
the DOM onto the DOM. For example, dragging an image from the OS file
manager onto a file-upload input element. This does not implement the
ability to drag DOM elements.
2024-08-19 13:29:19 +02:00

142 lines
4.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2023, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/VM.h>
#include <LibWeb/Bindings/InternalsPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Event.h>
#include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/HTMLElement.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/Internals/Internals.h>
#include <LibWeb/Page/InputEvent.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/Painting/PaintableBox.h>
#include <LibWeb/Painting/ViewportPaintable.h>
namespace Web::Internals {
JS_DEFINE_ALLOCATOR(Internals);
Internals::Internals(JS::Realm& realm)
: Bindings::PlatformObject(realm)
{
}
Internals::~Internals() = default;
void Internals::initialize(JS::Realm& realm)
{
Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(Internals);
}
void Internals::signal_text_test_is_done()
{
global_object().browsing_context()->page().client().page_did_finish_text_test();
}
void Internals::gc()
{
vm().heap().collect_garbage();
}
JS::Object* Internals::hit_test(double x, double y)
{
auto* active_document = global_object().browsing_context()->top_level_browsing_context()->active_document();
// NOTE: Force a layout update just before hit testing. This is because the current layout tree, which is required
// for stacking context traversal, might not exist if this call occurs between the tear_down_layout_tree()
// and update_layout() calls
active_document->update_layout();
auto result = active_document->paintable_box()->hit_test({ x, y }, Painting::HitTestType::Exact);
if (result.has_value()) {
auto hit_tеsting_result = JS::Object::create(realm(), nullptr);
hit_tеsting_result->define_direct_property("node", result->dom_node(), JS::default_attributes);
hit_tеsting_result->define_direct_property("indexInNode", JS::Value(result->index_in_node), JS::default_attributes);
return hit_tеsting_result;
}
return nullptr;
}
void Internals::send_text(HTML::HTMLElement& target, String const& text)
{
auto& page = global_object().browsing_context()->page();
target.focus();
for (auto code_point : text.code_points())
page.handle_keydown(UIEvents::code_point_to_key_code(code_point), 0, code_point);
}
void Internals::commit_text()
{
global_object().browsing_context()->page().handle_keydown(UIEvents::Key_Return, 0, 0);
}
void Internals::click(double x, double y)
{
click(x, y, UIEvents::MouseButton::Primary);
}
void Internals::middle_click(double x, double y)
{
click(x, y, UIEvents::MouseButton::Middle);
}
void Internals::click(double x, double y, UIEvents::MouseButton button)
{
auto& page = global_object().browsing_context()->page();
page.handle_mousedown({ x, y }, { x, y }, button, 0, 0);
page.handle_mouseup({ x, y }, { x, y }, button, 0, 0);
}
void Internals::move_pointer_to(double x, double y)
{
auto& page = global_object().browsing_context()->page();
page.handle_mousemove({ x, y }, { x, y }, 0, 0);
}
void Internals::wheel(double x, double y, double delta_x, double delta_y)
{
auto& page = global_object().browsing_context()->page();
page.handle_mousewheel({ x, y }, { x, y }, 0, 0, 0, delta_x, delta_y);
}
WebIDL::ExceptionOr<bool> Internals::dispatch_user_activated_event(DOM::EventTarget& target, DOM::Event& event)
{
event.set_is_trusted(true);
return target.dispatch_event(event);
}
JS::NonnullGCPtr<InternalAnimationTimeline> Internals::create_internal_animation_timeline()
{
auto& realm = this->realm();
return realm.heap().allocate<InternalAnimationTimeline>(realm, realm);
}
void Internals::simulate_drag_start(double x, double y, String const& name, String const& contents)
{
Vector<HTML::SelectedFile> files;
files.empend(name.to_byte_string(), MUST(ByteBuffer::copy(contents.bytes())));
auto& page = global_object().browsing_context()->page();
page.handle_drag_and_drop_event(DragEvent::Type::DragStart, { x, y }, { x, y }, UIEvents::MouseButton::Primary, 0, 0, move(files));
}
void Internals::simulate_drag_move(double x, double y)
{
auto& page = global_object().browsing_context()->page();
page.handle_drag_and_drop_event(DragEvent::Type::DragMove, { x, y }, { x, y }, UIEvents::MouseButton::Primary, 0, 0, {});
}
void Internals::simulate_drop(double x, double y)
{
auto& page = global_object().browsing_context()->page();
page.handle_drag_and_drop_event(DragEvent::Type::Drop, { x, y }, { x, y }, UIEvents::MouseButton::Primary, 0, 0, {});
}
}