[Chromium Telemetry]-Add the CDP Fetch domain to the inspector
The Fetch domain can be directly applied in the following scenario:
In some third-party pages, the data generated during the testing process
is not exposed for direct access by Telemetry. However, if the frontend
page needs to upload the test data via a network request, Telemetry can
intercept the network request for data reporting through the Fetch
domain and obtain the test data of this session.
Change-Id: Iebe4f43310231b6d55033db61643f53ec79ef2d9
Reviewed-on: https://chromium-review.googlesource.com/c/catapult/+/7030783
Reviewed-by: Mikhail Khokhlov <khokhlov@google.com>
Commit-Queue: Peng Zhou <zhoupeng.1996@bytedance.com>
Reviewed-by: Wenbin Zhang <wenbinzhang@google.com>
diff --git a/telemetry/telemetry/internal/actions/action_runner.py b/telemetry/telemetry/internal/actions/action_runner.py
index bad6281..7908c30 100644
--- a/telemetry/telemetry/internal/actions/action_runner.py
+++ b/telemetry/telemetry/internal/actions/action_runner.py
@@ -49,6 +49,8 @@
# that Java Heap garbage collection can take ~5 seconds to complete.
_GARBAGE_COLLECTION_PROPAGATION_TIME = 6
+# Timeout for websocket communication when using inspector
+_DEFAULT_WEBSOCKET_TIMEOUT = 60
ActionRunnerBase = six.with_metaclass(trace_event.TracedMetaClass, object)
@@ -873,6 +875,43 @@
"""Stops emulation of a mobile device."""
self._tab.StopMobileDeviceEmulation(timeout)
+ def EnableFetch(self,
+ patterns,
+ request_paused_callback=None,
+ auth_required_callback=None,
+ timeout=_DEFAULT_WEBSOCKET_TIMEOUT):
+ self._tab.EnableFetch(patterns, request_paused_callback,
+ auth_required_callback, timeout)
+
+ def DisableFetch(self, timeout=_DEFAULT_WEBSOCKET_TIMEOUT):
+ self._tab.DisableFetch(timeout)
+
+ def CreateContinueRequest(self,
+ request_id,
+ url=None,
+ method=None,
+ post_data=None,
+ headers=None):
+ return self._tab.CreateContinueRequest(request_id, url, method, post_data,
+ headers)
+
+ def ContinueRequestSync(self, request, timeout=_DEFAULT_WEBSOCKET_TIMEOUT):
+ return self._tab.ContinueRequestSync(request, timeout)
+
+ def ContinueRequestAndIgnoreResponse(self, request):
+ self._tab.ContinueRequestAndIgnoreResponse(request)
+
+ def CreateContinueWithAuthRequest(self, request_id, auth_challenge_response):
+ return self._tab.CreateContinueWithAuthRequest(request_id,
+ auth_challenge_response)
+
+ def ContinueWithAuthRequestSync(self,
+ request,
+ timeout=_DEFAULT_WEBSOCKET_TIMEOUT):
+ return self._tab.ContinueWithAuthRequestSync(request, timeout)
+
+ def ContinueWithAuthRequestAndIgnoreResponse(self, request):
+ self._tab.ContinueWithAuthRequestAndIgnoreResponse(request)
class Interaction():
diff --git a/telemetry/telemetry/internal/backends/chrome_inspector/inspector_backend.py b/telemetry/telemetry/internal/backends/chrome_inspector/inspector_backend.py
index 6f31366..726ffa3 100644
--- a/telemetry/telemetry/internal/backends/chrome_inspector/inspector_backend.py
+++ b/telemetry/telemetry/internal/backends/chrome_inspector/inspector_backend.py
@@ -19,6 +19,7 @@
from telemetry import decorators
from telemetry.internal.backends.chrome_inspector import devtools_http
from telemetry.internal.backends.chrome_inspector import inspector_console
+from telemetry.internal.backends.chrome_inspector import inspector_fetch
from telemetry.internal.backends.chrome_inspector import inspector_log
from telemetry.internal.backends.chrome_inspector import inspector_memory
from telemetry.internal.backends.chrome_inspector import inspector_page
@@ -78,6 +79,7 @@
try:
self._websocket.Connect(self.debugger_url, timeout)
self._console = inspector_console.InspectorConsole(self._websocket)
+ self._fetch = inspector_fetch.InspectorFetch(self._websocket)
self._log = inspector_log.InspectorLog(self._websocket)
self._memory = inspector_memory.InspectorMemory(self._websocket)
self._runtime = inspector_runtime.InspectorRuntime(self._websocket)
@@ -945,3 +947,47 @@
@_HandleInspectorWebSocketExceptions
def CollectGarbage(self, timeout_in_seconds=60):
self._page.CollectGarbage(timeout_in_seconds)
+
+ @_HandleInspectorWebSocketExceptions
+ def EnableFetch(self,
+ patterns,
+ request_paused_callback=None,
+ auth_required_callback=None,
+ timeout=60):
+ self._fetch.EnableFetch(patterns, request_paused_callback,
+ auth_required_callback, timeout)
+
+ @_HandleInspectorWebSocketExceptions
+ def DisableFetch(self, timeout=60):
+ self._fetch.DisableFetch(timeout)
+
+ @_HandleInspectorWebSocketExceptions
+ def CreateContinueRequest(self,
+ request_id,
+ url=None,
+ method=None,
+ post_data=None,
+ headers=None):
+ return self._fetch.CreateContinueRequest(request_id, url, method, post_data,
+ headers)
+
+ @_HandleInspectorWebSocketExceptions
+ def ContinueRequestSync(self, request, timeout=60):
+ return self._fetch.ContinueRequestSync(request, timeout)
+
+ @_HandleInspectorWebSocketExceptions
+ def ContinueRequestAndIgnoreResponse(self, request):
+ self._fetch.ContinueRequestAndIgnoreResponse(request)
+
+ @_HandleInspectorWebSocketExceptions
+ def CreateContinueWithAuthRequest(self, request_id, auth_challenge_response):
+ return self._fetch.CreateContinueWithAuthRequest(request_id,
+ auth_challenge_response)
+
+ @_HandleInspectorWebSocketExceptions
+ def ContinueWithAuthRequestSync(self, request, timeout=60):
+ return self._fetch.ContinueWithAuthRequestSync(request, timeout)
+
+ @_HandleInspectorWebSocketExceptions
+ def ContinueWithAuthRequestAndIgnoreResponse(self, request):
+ self._fetch.ContinueWithAuthRequestAndIgnoreResponse(request)
diff --git a/telemetry/telemetry/internal/backends/chrome_inspector/inspector_fetch.py b/telemetry/telemetry/internal/backends/chrome_inspector/inspector_fetch.py
new file mode 100644
index 0000000..88de648
--- /dev/null
+++ b/telemetry/telemetry/internal/backends/chrome_inspector/inspector_fetch.py
@@ -0,0 +1,126 @@
+# Copyright 2025 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from __future__ import absolute_import
+
+from telemetry.core import exceptions
+
+# Timeout for websocket communication when using inspector
+_DEFAULT_WEBSOCKET_TIMEOUT = 60
+
+
+class InspectorFetchException(exceptions.Error):
+ pass
+
+
+class InspectorFetch():
+ """
+ Reference: https://chromedevtools.github.io/devtools-protocol/tot/Fetch/
+ """
+
+ def __init__(self, inspector_websocket):
+ self._inspector_websocket = inspector_websocket
+ self._inspector_websocket.RegisterDomain('Fetch', self._OnNotification)
+ self._request_paused_callback = None
+ self._auth_required_callback = None
+
+ def EnableFetch(self,
+ patterns,
+ request_paused_callback=None,
+ auth_required_callback=None,
+ timeout=60):
+ self._request_paused_callback = request_paused_callback
+ self._auth_required_callback = auth_required_callback
+ self._EnableFetch(patterns, timeout)
+
+ def DisableFetch(self, timeout=60):
+ self._DisableFetch(timeout)
+ self._request_paused_callback = None
+ self._auth_required_callback = None
+
+ def _OnNotification(self, message):
+ if message['method'] == 'Fetch.requestPaused':
+ if self._request_paused_callback:
+ self._request_paused_callback(message)
+
+ if message['method'] == 'Fetch.authRequired':
+ if self._auth_required_callback:
+ self._auth_required_callback(message)
+
+ def _EnableFetch(self, patterns, timeout):
+ request = {'method': 'Fetch.enable', 'params': {}}
+
+ if patterns:
+ params = {'patterns': patterns}
+ request['params'] = params
+ if self._auth_required_callback:
+ request['params']['handleAuthChallenge'] = True
+
+ res = self._inspector_websocket.SyncRequest(request, timeout)
+
+ if 'error' in res:
+ raise InspectorFetchException(res['error']['message'])
+
+ def _DisableFetch(self, timeout):
+ request = {'method': 'Fetch.disable'}
+ res = self._inspector_websocket.SyncRequest(request, timeout)
+
+ if 'error' in res:
+ raise InspectorFetchException(res['error']['message'])
+
+ def CreateContinueRequest(self,
+ request_id,
+ url=None,
+ method=None,
+ post_data=None,
+ headers=None):
+ request = {
+ 'method': 'Fetch.continueRequest',
+ 'params': {
+ 'requestId': request_id,
+ }
+ }
+ if url:
+ request['params']['url'] = url
+ if method:
+ request['params']['method'] = method
+ if post_data:
+ request['params']['postData'] = post_data
+ if headers:
+ request['params']['headers'] = headers
+
+ return request
+
+ def ContinueRequestSync(self, request, timeout=_DEFAULT_WEBSOCKET_TIMEOUT):
+ res = self._inspector_websocket.SyncRequest(request, timeout)
+
+ if 'error' in res:
+ raise InspectorFetchException(res['error']['message'])
+ return res
+
+ def ContinueRequestAndIgnoreResponse(self, request):
+ self._inspector_websocket.SendAndIgnoreResponse(request)
+
+ def CreateContinueWithAuthRequest(self, request_id, auth_challenge_response):
+ request = {
+ 'method': 'Fetch.continueWithAuth',
+ 'params': {
+ 'requestId': request_id,
+ 'authChallengeResponse': auth_challenge_response,
+ }
+ }
+
+ return request
+
+ def ContinueWithAuthRequestSync(self,
+ request,
+ timeout=_DEFAULT_WEBSOCKET_TIMEOUT):
+ res = self._inspector_websocket.SyncRequest(request, timeout)
+
+ if 'error' in res:
+ raise InspectorFetchException(res['error']['message'])
+ return res
+
+ def ContinueWithAuthRequestAndIgnoreResponse(self, request):
+ self._inspector_websocket.SendAndIgnoreResponse(request)
diff --git a/telemetry/telemetry/internal/browser/tab.py b/telemetry/telemetry/internal/browser/tab.py
index b42c381..e1daf9a 100644
--- a/telemetry/telemetry/internal/browser/tab.py
+++ b/telemetry/telemetry/internal/browser/tab.py
@@ -301,3 +301,39 @@
return self._inspector_backend.WaitForSharedStorageEvents(expected_events,
mode,
timeout)
+
+ def EnableFetch(self,
+ patterns,
+ request_paused_callback=None,
+ auth_required_callback=None,
+ timeout=DEFAULT_TAB_TIMEOUT):
+ self._inspector_backend.EnableFetch(patterns, request_paused_callback,
+ auth_required_callback, timeout)
+
+ def DisableFetch(self, timeout=DEFAULT_TAB_TIMEOUT):
+ self._inspector_backend.DisableFetch(timeout)
+
+ def CreateContinueRequest(self,
+ request_id,
+ url=None,
+ method=None,
+ post_data=None,
+ headers=None):
+ return self._inspector_backend.CreateContinueRequest(
+ request_id, url, method, post_data, headers)
+
+ def ContinueRequestSync(self, request, timeout=DEFAULT_TAB_TIMEOUT):
+ return self._inspector_backend.ContinueRequestSync(request, timeout)
+
+ def ContinueRequestAndIgnoreResponse(self, request):
+ self._inspector_backend.ContinueRequestAndIgnoreResponse(request)
+
+ def CreateContinueWithAuthRequest(self, request_id, auth_challenge_response):
+ return self._inspector_backend.CreateContinueWithAuthRequest(
+ request_id, auth_challenge_response)
+
+ def ContinueWithAuthRequestSync(self, request, timeout=DEFAULT_TAB_TIMEOUT):
+ return self._inspector_backend.ContinueWithAuthRequestSync(request, timeout)
+
+ def ContinueWithAuthRequestAndIgnoreResponse(self, request):
+ self._inspector_backend.ContinueWithAuthRequestAndIgnoreResponse(request)