ladybird/Libraries/LibDevTools/Actors/TabActor.cpp
Sam Atkins cbbd2e173e LibDevTools+LibWebView: Support DevTools URL navigation
Firefox's remote toolbox exposes an editable URL field for tab
descriptors. Submitting that field sends navigateTo to the tab
descriptor rather than the frame target.

Handle the request in the tab actor, route it through the DevTools
delegate, and reuse WebView's URL sanitizing and load path. Add protocol
coverage so the toolbar navigation request stays wired up.
2026-06-01 15:09:25 +01:00

118 lines
3.6 KiB
C++

/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/JsonObject.h>
#include <LibDevTools/Actors/TabActor.h>
#include <LibDevTools/Actors/WatcherActor.h>
#include <LibDevTools/DevToolsDelegate.h>
#include <LibDevTools/DevToolsServer.h>
namespace DevTools {
NonnullRefPtr<TabActor> TabActor::create(DevToolsServer& devtools, String name, TabDescription description)
{
return adopt_ref(*new TabActor(devtools, move(name), move(description)));
}
TabActor::TabActor(DevToolsServer& devtools, String name, TabDescription description)
: Actor(devtools, move(name))
, m_description(move(description))
{
}
TabActor::~TabActor()
{
reset_selected_node();
}
void TabActor::handle_message(Message const& message)
{
JsonObject response;
if (message.type == "getFavicon"sv) {
// FIXME: Firefox DevTools wants a favicon URL here, but supplying a URL seems to prevent this tab from being
// listed on the about:debugging page. Both Servo and Firefox itself supply `null` here.
response.set("favicon"sv, JsonValue {});
send_response(message, move(response));
return;
}
if (message.type == "getWatcher"sv) {
if (!m_watcher)
m_watcher = devtools().register_actor<WatcherActor>(this);
response.set("actor"sv, m_watcher->name());
response.set("traits"sv, m_watcher->serialize_description());
send_response(message, move(response));
return;
}
if (message.type == "reloadDescriptor"sv) {
auto bypass_cache = message.data.get_bool("bypassCache"sv).value_or(false);
devtools().delegate().reload_tab(m_description, bypass_cache);
send_response(message, move(response));
return;
}
if (message.type == "navigateTo"sv) {
auto url = get_required_parameter<String>(message, "url"sv);
if (!url.has_value())
return;
devtools().delegate().navigate_tab(m_description, *url);
send_response(message, move(response));
return;
}
if (message.type == "goBack"sv) {
devtools().delegate().traverse_the_history_by_delta(m_description, -1);
send_response(message, move(response));
return;
}
if (message.type == "goForward"sv) {
devtools().delegate().traverse_the_history_by_delta(m_description, 1);
send_response(message, move(response));
return;
}
send_unrecognized_packet_type_error(message);
}
JsonObject TabActor::serialize_description() const
{
JsonObject traits;
traits.set("watcher"sv, true);
traits.set("supportsReloadDescriptor"sv, true);
traits.set("supportsNavigation"sv, true);
// FIXME: We are using the tab's ID multiple times here. This is likely not correct, as both Firefox and Servo
// provide different IDs for browserId, browsingContextID, and outerWindowID.
JsonObject description;
description.set("actor"sv, name());
description.set("title"sv, m_description.title);
description.set("url"sv, m_description.url);
description.set("browserId"sv, m_description.id);
description.set("browsingContextID"sv, m_description.id);
description.set("outerWindowID"sv, m_description.id);
description.set("traits"sv, move(traits));
return description;
}
void TabActor::reset_selected_node()
{
devtools().delegate().clear_highlighted_dom_node(description());
devtools().delegate().clear_inspected_dom_node(description());
}
void TabActor::navigate_to(String url, String title)
{
m_description.url = move(url);
m_description.title = move(title);
++m_inner_window_id;
}
}