DevTools: Implement support for showing source_content in Debugger > Source panel (#36774)

This patch adds support for showing source_content in `Debugger >
Source` panel. This works by handling the clients `source` messages in
the source actor. These source actors are already advertised as resource
via the watcher, populating the source list. We also update the
`sources` handler in thread actor for future work in thread debugging.

Note: while this PR also adds support for showing worker script
source_content, worker has been broken (See
https://github.com/servo/servo/issues/37012). I was able to confirm the
`content_type` and `source_content` for worker script in logs.


![image](https://github.com/user-attachments/assets/bd53ea29-003a-4b5e-a3e8-6e280afa4671)

Fixes: part of https://github.com/servo/servo/issues/36027

---------

Signed-off-by: atbrakhi <atbrakhi@igalia.com>
This commit is contained in:
atbrakhi 2025-06-13 11:31:33 +02:00 committed by GitHub
parent 5159529888
commit 7a801f0ef5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 204 additions and 42 deletions

View file

@ -60,6 +60,7 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
f"{self.base_url}/classic.js",
f"{self.base_url}/test.html",
"https://servo.org/js/load-table.js",
f"{self.base_url}/test.html",
]
),
tuple([f"{self.base_url}/worker.js"]),
@ -92,6 +93,17 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
self.run_servoshell(url="data:text/html,<script type=module>;</script>")
self.assert_sources_list(1, set([tuple(["data:text/html,<script type=module>;</script>"])]))
def test_source_content_inline_script(self):
script_content = "console.log('Hello, world!');"
self.run_servoshell(url=f"data:text/html,<script>{script_content}</script>")
self.assert_source_content("data:text/html,<script>console.log('Hello, world!');</script>", script_content)
def test_source_content_external_script(self):
self.start_web_server(test_dir=os.path.join(DevtoolsTests.script_path, "devtools_tests/sources"))
self.run_servoshell(url=f'data:text/html,<script src="{self.base_url}/classic.js"></script>')
expected_content = 'console.log("external classic");\n'
self.assert_source_content(f"{self.base_url}/classic.js", expected_content)
# Sets `base_url` and `web_server` and `web_server_thread`.
def start_web_server(self, *, test_dir=None):
if test_dir is None:
@ -145,7 +157,7 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
if self.base_url is not None:
self.base_url = None
def assert_sources_list(self, expected_targets: int, expected_urls_by_target: set[tuple[str]]):
def _setup_devtools_client(self, expected_targets=1):
client = RDPClient()
client.connect("127.0.0.1", 6080)
root = RootActor(client)
@ -179,6 +191,11 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
result: Optional[Exception] = done.result(1)
if result:
raise result
return client, watcher, targets
def assert_sources_list(self, expected_targets: int, expected_urls_by_target: set[tuple[str]]):
client, watcher, targets = self._setup_devtools_client(expected_targets)
done = Future()
# NOTE: breaks if two targets have the same list of source urls.
# This should really be a multiset, but Python does not have multisets.
@ -212,6 +229,45 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
self.assertEqual(actual_urls_by_target, expected_urls_by_target)
client.disconnect()
def assert_source_content(self, source_url: str, expected_content: str):
client, watcher, targets = self._setup_devtools_client()
done = Future()
source_actors = {}
def on_source_resource(data):
for [resource_type, sources] in data["array"]:
try:
self.assertEqual(resource_type, "source")
for source in sources:
if source["url"] == source_url:
source_actors[source_url] = source["actor"]
done.set_result(None)
except Exception as e:
done.set_result(e)
for target in targets:
client.add_event_listener(
target["actor"],
Events.Watcher.RESOURCES_AVAILABLE_ARRAY,
on_source_resource,
)
watcher.watch_resources([Resources.SOURCE])
result: Optional[Exception] = done.result(1)
if result:
raise result
# We found at least one source with the given url.
self.assertIn(source_url, source_actors)
source_actor = source_actors[source_url]
response = client.send_receive({"to": source_actor, "type": "source"})
self.assertEqual(response["source"], expected_content)
client.disconnect()
def run_tests(script_path):
DevtoolsTests.script_path = script_path