mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 12:19:54 +00:00
LibDevTools: Implement enough of the protocol to see a tab list
Previously, we could connect to our DevTools server from Firefox, but could not see any information on Ladybird's opened tabs. This implements enough of the protocol to see a tab list, but we cannot yet inspect the tabs.
This commit is contained in:
parent
58bc44ba2a
commit
b974e91731
Notes:
github-actions[bot]
2025-02-19 13:47:18 +00:00
Author: https://github.com/trflynn89
Commit: b974e91731
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3589
Reviewed-by: https://github.com/ADKaster
9 changed files with 168 additions and 2 deletions
|
@ -9,6 +9,7 @@
|
|||
#include <LibDevTools/Actors/PreferenceActor.h>
|
||||
#include <LibDevTools/Actors/ProcessActor.h>
|
||||
#include <LibDevTools/Actors/RootActor.h>
|
||||
#include <LibDevTools/Actors/TabActor.h>
|
||||
#include <LibDevTools/DevToolsDelegate.h>
|
||||
#include <LibDevTools/DevToolsServer.h>
|
||||
|
||||
|
@ -88,7 +89,23 @@ void RootActor::handle_message(StringView type, JsonObject const& message)
|
|||
}
|
||||
|
||||
if (type == "getTab"sv) {
|
||||
response.set("tab"sv, JsonObject {});
|
||||
auto browser_id = message.get_integer<u64>("browserId"sv);
|
||||
if (!browser_id.has_value()) {
|
||||
send_missing_parameter_error("browserId"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto const& actor : devtools().actor_registry()) {
|
||||
auto const* tab_actor = as_if<TabActor>(*actor.value);
|
||||
if (!tab_actor)
|
||||
continue;
|
||||
if (tab_actor->description().id != *browser_id)
|
||||
continue;
|
||||
|
||||
response.set("tab"sv, tab_actor->serialize_description());
|
||||
break;
|
||||
}
|
||||
|
||||
send_message(move(response));
|
||||
return;
|
||||
}
|
||||
|
@ -119,7 +136,16 @@ void RootActor::handle_message(StringView type, JsonObject const& message)
|
|||
}
|
||||
|
||||
if (type == "listTabs"sv) {
|
||||
response.set("tabs"sv, JsonArray {});
|
||||
m_has_sent_tab_list_changed_since_last_list_tabs_request = false;
|
||||
|
||||
JsonArray tabs;
|
||||
|
||||
for (auto& tab_description : devtools().delegate().tab_list()) {
|
||||
auto& actor = devtools().register_actor<TabActor>(move(tab_description));
|
||||
tabs.must_append(actor.serialize_description());
|
||||
}
|
||||
|
||||
response.set("tabs"sv, move(tabs));
|
||||
send_message(move(response));
|
||||
return;
|
||||
}
|
||||
|
@ -133,4 +159,17 @@ void RootActor::handle_message(StringView type, JsonObject const& message)
|
|||
send_unrecognized_packet_type_error(type);
|
||||
}
|
||||
|
||||
void RootActor::send_tab_list_changed_message()
|
||||
{
|
||||
if (m_has_sent_tab_list_changed_since_last_list_tabs_request)
|
||||
return;
|
||||
|
||||
JsonObject message;
|
||||
message.set("from"sv, name());
|
||||
message.set("type"sv, "tabListChanged"sv);
|
||||
send_message(move(message));
|
||||
|
||||
m_has_sent_tab_list_changed_since_last_list_tabs_request = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,8 +20,14 @@ public:
|
|||
|
||||
virtual void handle_message(StringView type, JsonObject const&) override;
|
||||
|
||||
void send_tab_list_changed_message();
|
||||
|
||||
private:
|
||||
RootActor(DevToolsServer&, ByteString name);
|
||||
|
||||
// https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#the-request-reply-notify-pattern
|
||||
// the root actor sends at most one "tabListChanged" notification after each "listTabs" request.
|
||||
bool m_has_sent_tab_list_changed_since_last_list_tabs_request { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
61
Libraries/LibDevTools/Actors/TabActor.cpp
Normal file
61
Libraries/LibDevTools/Actors/TabActor.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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/DevToolsServer.h>
|
||||
|
||||
namespace DevTools {
|
||||
|
||||
NonnullRefPtr<TabActor> TabActor::create(DevToolsServer& devtools, ByteString name, TabDescription description)
|
||||
{
|
||||
return adopt_ref(*new TabActor(devtools, move(name), move(description)));
|
||||
}
|
||||
|
||||
TabActor::TabActor(DevToolsServer& devtools, ByteString name, TabDescription description)
|
||||
: Actor(devtools, move(name))
|
||||
, m_description(move(description))
|
||||
{
|
||||
}
|
||||
|
||||
TabActor::~TabActor() = default;
|
||||
|
||||
void TabActor::handle_message(StringView type, JsonObject const&)
|
||||
{
|
||||
JsonObject response;
|
||||
response.set("from"sv, name());
|
||||
|
||||
if (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_message(move(response));
|
||||
return;
|
||||
}
|
||||
|
||||
send_unrecognized_packet_type_error(type);
|
||||
}
|
||||
|
||||
JsonObject TabActor::serialize_description() const
|
||||
{
|
||||
JsonObject traits;
|
||||
traits.set("watcher"sv, true);
|
||||
traits.set("supportsReloadDescriptor"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;
|
||||
}
|
||||
|
||||
}
|
38
Libraries/LibDevTools/Actors/TabActor.h
Normal file
38
Libraries/LibDevTools/Actors/TabActor.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <LibDevTools/Actor.h>
|
||||
|
||||
namespace DevTools {
|
||||
|
||||
struct TabDescription {
|
||||
u64 id { 0 };
|
||||
ByteString title;
|
||||
ByteString url;
|
||||
};
|
||||
|
||||
class TabActor final : public Actor {
|
||||
public:
|
||||
static constexpr auto base_name = "tab"sv;
|
||||
|
||||
static NonnullRefPtr<TabActor> create(DevToolsServer&, ByteString name, TabDescription);
|
||||
virtual ~TabActor() override;
|
||||
|
||||
virtual void handle_message(StringView type, JsonObject const&) override;
|
||||
|
||||
TabDescription const& description() const { return m_description; }
|
||||
JsonObject serialize_description() const;
|
||||
|
||||
private:
|
||||
TabActor(DevToolsServer&, ByteString name, TabDescription);
|
||||
|
||||
TabDescription m_description;
|
||||
};
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ set(SOURCES
|
|||
Actors/PreferenceActor.cpp
|
||||
Actors/ProcessActor.cpp
|
||||
Actors/RootActor.cpp
|
||||
Actors/TabActor.cpp
|
||||
Connection.cpp
|
||||
DevToolsServer.cpp
|
||||
)
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <LibDevTools/Actors/TabActor.h>
|
||||
#include <LibDevTools/Forward.h>
|
||||
|
||||
namespace DevTools {
|
||||
|
@ -13,6 +15,8 @@ namespace DevTools {
|
|||
class DevToolsDelegate {
|
||||
public:
|
||||
virtual ~DevToolsDelegate() = default;
|
||||
|
||||
virtual Vector<TabDescription> tab_list() const { return {}; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <LibDevTools/Actors/DeviceActor.h>
|
||||
#include <LibDevTools/Actors/PreferenceActor.h>
|
||||
#include <LibDevTools/Actors/ProcessActor.h>
|
||||
#include <LibDevTools/Actors/TabActor.h>
|
||||
#include <LibDevTools/Connection.h>
|
||||
#include <LibDevTools/DevToolsServer.h>
|
||||
|
||||
|
@ -43,6 +44,18 @@ DevToolsServer::DevToolsServer(DevToolsDelegate& delegate, NonnullRefPtr<Core::T
|
|||
|
||||
DevToolsServer::~DevToolsServer() = default;
|
||||
|
||||
void DevToolsServer::refresh_tab_list()
|
||||
{
|
||||
if (!m_root_actor)
|
||||
return;
|
||||
|
||||
m_actor_registry.remove_all_matching([](auto const&, auto const& actor) {
|
||||
return is<TabActor>(*actor);
|
||||
});
|
||||
|
||||
m_root_actor->send_tab_list_changed_message();
|
||||
}
|
||||
|
||||
ErrorOr<void> DevToolsServer::on_new_client()
|
||||
{
|
||||
if (m_connection)
|
||||
|
|
|
@ -45,6 +45,8 @@ public:
|
|||
return actor;
|
||||
}
|
||||
|
||||
void refresh_tab_list();
|
||||
|
||||
private:
|
||||
explicit DevToolsServer(DevToolsDelegate&, NonnullRefPtr<Core::TCPServer>);
|
||||
|
||||
|
|
|
@ -16,7 +16,9 @@ class DevToolsServer;
|
|||
class PreferenceActor;
|
||||
class ProcessActor;
|
||||
class RootActor;
|
||||
class TabActor;
|
||||
|
||||
struct ProcessDescription;
|
||||
struct TabDescription;
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue