refactor(inspector): derive response IDs and make parsing explicit

This commit is contained in:
ldm0
2026-11-12 03:32:12 +08:00
committed by Donough Liu
parent 166de628de
commit b20ff14ff9
22 changed files with 219 additions and 208 deletions
+11 -12
View File
@@ -126,18 +126,17 @@ pub use moli_renderer_v8::{
RendererRuntimeInspectorIoCommandClaim, RendererRuntimeInspectorIoCommandRoute,
RendererRuntimeInspectorMainCommandCompletion, RendererRuntimeInspectorMainCommandRoute,
RendererRuntimeInspectorMessage, RendererRuntimeInspectorMessageBatch,
RendererRuntimeInspectorProtocolMessage, RendererRuntimeInspectorProtocolMessageValueMut,
RendererRuntimeObservableSourceItem, RendererRuntimeObservableSourceSummary,
RendererRuntimeRealmInfo, RendererScreenshotClip, RendererScreenshotFormat,
RendererScreenshotRegion, RendererScrollIntoViewResult, RendererServiceWorkerConsoleMessage,
RendererServiceWorkerExceptionMessage, RendererServiceWorkerFetchDiagnostic,
RendererServiceWorkerFetchDiagnosticResult, RendererServiceWorkerRunIdentity,
RendererServiceWorkerTargetEvent, RendererServiceWorkerTargetInfo,
RendererServiceWorkerVersionStatus, RendererSetDocumentContentResult,
RendererSharedWorkerConsoleMessage, RendererSharedWorkerTargetEvent,
RendererSharedWorkerTargetInfo, RendererSyntheticResponseBody, RendererTextSearchMatch,
RendererTouchPoint, RendererVisualStateToken, RendererWindowDocumentSource,
RuntimeConsoleMessageSnapshot, WorkerInspectorError,
RendererRuntimeInspectorProtocolMessage, RendererRuntimeObservableSourceItem,
RendererRuntimeObservableSourceSummary, RendererRuntimeRealmInfo, RendererScreenshotClip,
RendererScreenshotFormat, RendererScreenshotRegion, RendererScrollIntoViewResult,
RendererServiceWorkerConsoleMessage, RendererServiceWorkerExceptionMessage,
RendererServiceWorkerFetchDiagnostic, RendererServiceWorkerFetchDiagnosticResult,
RendererServiceWorkerRunIdentity, RendererServiceWorkerTargetEvent,
RendererServiceWorkerTargetInfo, RendererServiceWorkerVersionStatus,
RendererSetDocumentContentResult, RendererSharedWorkerConsoleMessage,
RendererSharedWorkerTargetEvent, RendererSharedWorkerTargetInfo, RendererSyntheticResponseBody,
RendererTextSearchMatch, RendererTouchPoint, RendererVisualStateToken,
RendererWindowDocumentSource, RuntimeConsoleMessageSnapshot, WorkerInspectorError,
};
pub use moli_renderer_v8::{
RendererAppManifest, RendererAppManifestDisplayMode, RendererAppManifestError,
+1 -1
View File
@@ -5597,7 +5597,7 @@ fn runtime_protocol_response_by_id(
) -> Option<&serde_json::Value> {
messages.iter().find_map(|message| match message {
RendererRuntimeInspectorMessage::Protocol(value)
if value.get("id").and_then(serde_json::Value::as_i64) == Some(response_id) =>
if value.payload().id_i64() == Some(response_id) =>
{
Some(value.value())
}
-7
View File
@@ -13,7 +13,6 @@ use serde_json::{Number, Value, value::RawValue};
use std::{
collections::BTreeMap,
fmt,
ops::Deref,
sync::{Arc, OnceLock},
};
@@ -336,12 +335,6 @@ impl From<Value> for InspectorJson {
Self::Parsed(value)
}
}
impl Deref for InspectorJson {
type Target = Value;
fn deref(&self) -> &Value {
self.value()
}
}
impl Serialize for InspectorJson {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
@@ -40,6 +40,7 @@ impl CdpFrontendRoutingState {
flattened_protocol,
} => {
if let Some(child_session_id) = message
.value()
.pointer("/result/sessionId")
.and_then(Value::as_str)
.map(str::to_owned)
@@ -77,11 +78,11 @@ impl CdpFrontendRoutingState {
let target_event_session_id = method
.as_deref()
.filter(|method| method.starts_with("Target."))
.and_then(|_| message.pointer("/params/sessionId"))
.and_then(|_| message.value().pointer("/params/sessionId"))
.and_then(Value::as_str)
.map(str::to_owned);
let target_event_target_id = (method.as_deref() == Some("Target.attachedToTarget"))
.then(|| message.pointer("/params/targetInfo/targetId"))
.then(|| message.value().pointer("/params/targetInfo/targetId"))
.flatten()
.and_then(Value::as_str)
.map(str::to_owned);
@@ -126,8 +126,8 @@ fn browser_and_page_client_command_ids_are_isolated_and_restored() {
)
.expect("route browser response");
assert_eq!(browser_frontend.frontend_id, 5);
assert_eq!(browser_response["id"], json!(7));
assert!(browser_response.get("sessionId").is_none());
assert_eq!(browser_response.id_u64(), Some(7));
assert!(browser_response.session_id().is_none());
let (page_frontend, page_response) = routing
.route_message(
@@ -141,8 +141,8 @@ fn browser_and_page_client_command_ids_are_isolated_and_restored() {
)
.expect("route page response");
assert_eq!(page_frontend.frontend_id, 10);
assert_eq!(page_response["id"], json!(7));
assert!(page_response.get("sessionId").is_none());
assert_eq!(page_response.id_u64(), Some(7));
assert!(page_response.session_id().is_none());
}
#[test]
@@ -187,9 +187,9 @@ fn browser_frontends_with_the_same_client_command_id_route_independently() {
)
.expect("route second response");
assert_eq!(frontend.frontend_id(), 6);
assert_eq!(response["id"], json!(7));
assert_eq!(response["result"]["product"], json!("second"));
assert!(response.get("sessionId").is_none());
assert_eq!(response.id_u64(), Some(7));
assert_eq!(response.value()["result"]["product"], json!("second"));
assert!(response.session_id().is_none());
let (frontend, response) = routing
.route_message(
@@ -203,9 +203,9 @@ fn browser_frontends_with_the_same_client_command_id_route_independently() {
)
.expect("route first response");
assert_eq!(frontend.frontend_id(), 5);
assert_eq!(response["id"], json!(7));
assert_eq!(response["result"]["product"], json!("first"));
assert!(response.get("sessionId").is_none());
assert_eq!(response.id_u64(), Some(7));
assert_eq!(response.value()["result"]["product"], json!("first"));
assert!(response.session_id().is_none());
}
#[test]
@@ -230,7 +230,7 @@ fn browser_base_session_events_are_private_and_frontend_scoped() {
)
.expect("route first browser event");
assert_eq!(frontend.frontend_id(), 5);
assert!(event.get("sessionId").is_none());
assert!(event.session_id().is_none());
assert!(
routing
@@ -273,7 +273,7 @@ fn root_detach_with_a_known_child_routes_to_its_exact_frontend() {
)
.expect("route owner-qualified root detach");
assert_eq!(frontend.frontend_id(), 5);
assert!(event.get("sessionId").is_none());
assert!(event.session_id().is_none());
assert!(matches!(
routing.prepare_command(
@@ -333,7 +333,7 @@ fn root_detach_restores_visible_parent_for_nested_child() {
None,
)
.expect("route nested root detach");
assert_eq!(event["sessionId"], json!("SID-child"));
assert_eq!(event.session_id(), Some("SID-child"));
}
#[test]
@@ -397,7 +397,7 @@ fn unregistering_one_browser_drops_only_its_pending_commands() {
)
.expect("second browser response remains routable");
assert_eq!(frontend.frontend_id(), 6);
assert_eq!(response["id"], json!(1));
assert_eq!(response.id_u64(), Some(1));
}
#[test]
@@ -444,7 +444,7 @@ fn browser_child_session_is_preserved_on_browser_frontend() {
None,
)
.expect("route child response");
assert_eq!(response["sessionId"], json!("SID-client-child"));
assert_eq!(response.session_id(), Some("SID-client-child"));
}
#[test]
@@ -779,8 +779,8 @@ fn attached_event_registers_child_before_attach_response() {
)
.expect("route attached event");
assert_eq!(frontend.frontend_id(), 5);
assert!(event.get("sessionId").is_none());
assert_eq!(event["params"]["sessionId"], json!("SID-child"));
assert!(event.session_id().is_none());
assert_eq!(event.value()["params"]["sessionId"], json!("SID-child"));
let child_command = expect_prepared_command(
routing.prepare_command(
@@ -813,8 +813,8 @@ fn attached_event_registers_child_before_attach_response() {
None,
)
.expect("route attach response");
assert_eq!(response["id"], json!(30));
assert!(response.get("sessionId").is_none());
assert_eq!(response.id_u64(), Some(30));
assert!(response.session_id().is_none());
}
#[test]
@@ -847,8 +847,8 @@ fn browser_frontend_hides_base_session_on_wire() {
None,
)
.expect("route browser response");
assert_eq!(response["id"], json!(9));
assert!(response.get("sessionId").is_none());
assert_eq!(response.id_u64(), Some(9));
assert!(response.session_id().is_none());
}
#[test]
@@ -975,7 +975,10 @@ fn private_page_session_detach_does_not_fall_back_to_browser_frontend() {
)
.expect("route browser-owned target detach");
assert_eq!(frontend.frontend_id, 5);
assert_eq!(message["params"]["sessionId"], json!("SID-browser-child"));
assert_eq!(
message.value()["params"]["sessionId"],
json!("SID-browser-child")
);
}
#[test]
@@ -83,9 +83,9 @@ fn legacy_transport_admits_actual_command_and_routes_its_background_reply_once()
.route_message(background.clone(), Some("SID-page-1"), None)
.unwrap();
assert_eq!(frontend.frontend_id(), 5);
assert!(event.get("sessionId").is_none());
assert!(event.session_id().is_none());
assert_eq!(
unwrap_message(&event, "SID-page-1"),
unwrap_message(event.value(), "SID-page-1"),
json!({"id":42,"result":{"loaderId":"LID-1"}})
);
assert!(
@@ -132,7 +132,7 @@ fn recursive_transport_preserves_ack_paths_and_explicit_payload_session() {
None,
)
.unwrap();
let inner = unwrap_message(&response, "SID-page-1");
let inner = unwrap_message(response.value(), "SID-page-1");
assert!(inner.get("sessionId").is_none());
assert_eq!(
unwrap_message(&inner, "SID-worker"),
@@ -163,9 +163,9 @@ fn transport_with_explicit_carrier_keeps_only_that_carrier_on_outer_event() {
let (_, response) = routing
.route_message(json!({"id":command.request().id(),"result":{}}), None, None)
.unwrap();
assert_eq!(response["sessionId"], "SID-page-1");
assert_eq!(response.session_id(), Some("SID-page-1"));
assert_eq!(
unwrap_message(&response, "SID-worker"),
unwrap_message(response.value(), "SID-worker"),
json!({"id":42,"result":{}})
);
}
@@ -247,7 +247,7 @@ fn nested_attach_keeps_normal_registration_and_disconnect_retires_pending_reply(
)
.unwrap();
assert_eq!(
unwrap_message(&response, "SID-page-1"),
unwrap_message(response.value(), "SID-page-1"),
json!({"id":42,"result":{"sessionId":"SID-other"}})
);
let (_, command) = target_command(
@@ -286,7 +286,7 @@ fn root_received_target_event_uses_owning_frontend_and_parent_carrier() {
"method":"Target.receivedMessageFromTarget","params":{"sessionId":session,"message":"{}"},
}),None, None).unwrap();
assert_eq!(frontend.frontend_id(), 5);
assert_eq!(response.get("sessionId").and_then(Value::as_str), parent);
assert_eq!(response.session_id(), parent);
}
assert!(routing.route_message(json!({"method":"Target.receivedMessageFromTarget","params":{"sessionId":"SID-missing","message":"{}"}}),None, None).is_none());
}
@@ -306,7 +306,7 @@ fn nonflat_attachment_routes_events_before_and_after_reply_without_losing_parent
Some(false),
)
.unwrap();
assert_eq!(attached["sessionId"], "SID-page-1");
assert_eq!(attached.session_id(), Some("SID-page-1"));
let enable = expect_prepared_command(
routing.prepare_command(
5,
@@ -335,18 +335,18 @@ fn nonflat_attachment_routes_events_before_and_after_reply_without_losing_parent
None,
)
.unwrap();
assert_eq!(event["sessionId"], "SID-page-1");
assert_eq!(event.session_id(), Some("SID-page-1"));
assert_eq!(
unwrap_message(&event, "SID-nonflat"),
unwrap_message(event.value(), "SID-nonflat"),
json!({"method":method,"params":params})
);
if method == "Runtime.executionContextCreated" {
let (_, reply) = routing
.route_message(json!({"id":enable.request().id(),"result":{}}), None, None)
.unwrap();
assert_eq!(reply["sessionId"], "SID-page-1");
assert_eq!(reply.session_id(), Some("SID-page-1"));
assert_eq!(
unwrap_message(&reply, "SID-nonflat"),
unwrap_message(reply.value(), "SID-nonflat"),
json!({"id":80,"result":{}})
);
}
@@ -382,8 +382,8 @@ fn nonflat_attachment_routes_events_before_and_after_reply_without_losing_parent
None,
)
.unwrap();
assert_eq!(reply["sessionId"], "SID-page-1");
assert_eq!(unwrap_message(&reply, "SID-nonflat")["id"], 81);
assert_eq!(reply.session_id(), Some("SID-page-1"));
assert_eq!(unwrap_message(reply.value(), "SID-nonflat")["id"], 81);
}
#[test]
@@ -401,8 +401,8 @@ fn mixed_flat_and_nonflat_ancestors_share_one_event_and_response_encoder() {
let (_, event) = routing.route_message(json!({
"method":"Runtime.executionContextCreated","sessionId":"SID-inner","params":{"context":{"id":3}},
}), Some("SID-inner"), None).unwrap();
assert!(event.get("sessionId").is_none());
let middle = unwrap_message(&event, "SID-outer");
assert!(event.session_id().is_none());
let middle = unwrap_message(event.value(), "SID-outer");
assert_eq!(middle["sessionId"], "SID-flat");
let inner = unwrap_message(&middle, "SID-inner");
assert!(inner.get("sessionId").is_none());
@@ -415,7 +415,7 @@ fn mixed_flat_and_nonflat_ancestors_share_one_event_and_response_encoder() {
)
.unwrap();
assert_eq!(
unwrap_message(&flat_event, "SID-outer")["sessionId"],
unwrap_message(flat_event.value(), "SID-outer")["sessionId"],
"SID-flat"
);
let (_, detached) = routing
@@ -426,7 +426,7 @@ fn mixed_flat_and_nonflat_ancestors_share_one_event_and_response_encoder() {
)
.unwrap();
assert_eq!(
unwrap_message(&detached, "SID-outer")["sessionId"],
unwrap_message(detached.value(), "SID-outer")["sessionId"],
"SID-flat"
);
assert!(routing.frontends.session("SID-inner").is_none());
@@ -464,7 +464,7 @@ fn nested_target_commands_share_implicit_session_policy_with_the_adapter() {
let (_, wrapped) = routing
.route_message(json!({"id":command.request().id(),"result":{}}), None, None)
.unwrap();
let reply = unwrap_message(&wrapped, "SID-page-1");
let reply = unwrap_message(wrapped.value(), "SID-page-1");
assert_eq!(reply["id"], 42);
assert_eq!(reply.get("sessionId").and_then(Value::as_str), explicit);
}
+13 -13
View File
@@ -236,12 +236,12 @@ impl RuntimeInspectorResponseReady {
Ok(completion) => {
let message =
runtime_command_output_response_message(&completion.output, completion.call_id)
.cloned()
.unwrap_or_else(|| Value::Null.into());
.map(InspectorJson::value)
.unwrap_or(&Value::Null);
let payload = runtime_inspector_mismatched_completion_payload(
command_id,
completion.call_id,
&message,
message,
);
(
completion.renderer_agent_attachment_id(),
@@ -287,12 +287,12 @@ impl RuntimeInspectorResponseReady {
Ok(completion) => {
let message =
runtime_command_output_response_message(&completion.output, completion.call_id)
.cloned()
.unwrap_or_else(|| Value::Null.into());
.map(InspectorJson::value)
.unwrap_or(&Value::Null);
runtime_inspector_mismatched_completion_payload(
self.frontend_command_id.get(),
completion.call_id,
&message,
message,
)
}
Err(message) => BackgroundCommandResponsePayload::internal_error(message.to_string()),
@@ -3077,10 +3077,10 @@ impl ProtocolDeliveryEnvelope {
) -> bool {
match &self.payload {
BackgroundProtocolEventPayload::Protocol(event) => {
event.message["method"] == json!("Console.messageAdded")
&& event.message["params"]["message"]["source"] == json!(source)
&& event.message["params"]["message"]["level"] == json!(level)
&& event.message["params"]["message"]["text"] == json!(text)
event.message.method() == Some("Console.messageAdded")
&& event.message.value()["params"]["message"]["source"] == json!(source)
&& event.message.value()["params"]["message"]["level"] == json!(level)
&& event.message.value()["params"]["message"]["text"] == json!(text)
}
BackgroundProtocolEventPayload::ConsoleMessageAdded(event) => {
event.source == source && event.level == level && event.text == text
@@ -5960,10 +5960,10 @@ mod tests {
}});
assert!(InspectorJson::from_slice(host.to_string().as_bytes()).is_err());
let wire = BackgroundProtocolEvent::immediate(host).into_cdp_message();
assert_eq!(wire["params"]["requestId"], "public-id");
assert_eq!(wire.value()["params"]["requestId"], "public-id");
for key in PRIVATE_PROTOCOL_PARAM_FIELDS {
assert_eq!(
wire["params"].get(*key).is_some(),
wire.value()["params"].get(*key).is_some(),
method == "Fetch.requestPaused" && *key == "__moliBlockedInterceptors"
);
}
@@ -5974,7 +5974,7 @@ mod tests {
.unwrap();
inspector.value_mut()["params"]["__moliFetchRequestId"] = json!("private-id");
let wire = BackgroundProtocolEvent::immediate(inspector).into_cdp_message();
assert!(wire["params"].get("__moliFetchRequestId").is_none());
assert!(wire.value()["params"].get("__moliFetchRequestId").is_none());
let inspector = InspectorJson::from_slice(
br#"{"method":"Runtime.consoleAPICalled","params":{"args":[{"value":{"__moliFetchRequestId":"\ud800"}}]}}"#,
)
+1 -1
View File
@@ -199,7 +199,7 @@ fn runtime_inspector_frontend_response(
let RendererRuntimeInspectorMessage::Protocol(message) = message else {
return None;
};
(message.get("id").and_then(Value::as_u64) == Some(command_id)).then(|| message.value())
(message.payload().id_u64() == Some(command_id)).then(|| message.value())
})
}
@@ -202,7 +202,10 @@ impl CdpConnection {
);
}
if Some(id) == current_cmd_id {
self.register_runtime_remote_object_ids_from_value_for_owner(owner, &message);
self.register_runtime_remote_object_ids_from_value_for_owner(
owner,
message.value(),
);
if matches!(message, InspectorJson::Raw(_)) {
message.set_session_id(current_session_id);
response_events.push(BackgroundProtocolEvent::immediate(message));
@@ -226,7 +229,7 @@ impl CdpConnection {
}
None => {
if message.method() == Some("Page.windowOpen") {
let params = message.get("params").unwrap_or(&Value::Null);
let params = message.value().get("params").unwrap_or(&Value::Null);
let Some(url) = params.get("url").and_then(Value::as_str) else {
tracing::warn!("dropping Page.windowOpen without a string url");
return false;
@@ -282,7 +285,10 @@ impl CdpConnection {
| "Runtime.executionContextsCleared"
)
) {
self.register_runtime_remote_object_ids_from_value_for_owner(owner, &message);
self.register_runtime_remote_object_ids_from_value_for_owner(
owner,
message.value(),
);
}
if matches!(
message.method(),
@@ -712,13 +718,13 @@ impl CdpConnection {
if let Some(object_group) = response.object_group() {
self.register_runtime_remote_object_ids_from_value_for_owner_with_group(
response.owner(),
&response.message,
response.message.value(),
object_group,
);
} else {
self.register_runtime_remote_object_ids_from_value_for_owner(
response.owner(),
&response.message,
response.message.value(),
);
}
let mut message = response.into_protocol_message();
@@ -767,7 +773,7 @@ impl CdpConnection {
return BidiChannelListenerRoute::Consumed;
}
let listener = residence.listener();
let message = &response.message;
let message = response.message.value();
if let Some(error) = message.get("error") {
tracing::debug!(
?error,
@@ -354,6 +354,7 @@ mod tests {
return None;
};
message
.value()
.get("params")
.and_then(|params| params.get("marker"))
.and_then(serde_json::Value::as_str)
@@ -13,8 +13,7 @@ pub(super) fn command_response_succeeded(
let RendererRuntimeInspectorMessage::Protocol(message) = message else {
return false;
};
message.get("id").and_then(Value::as_u64) == Some(command_id)
&& message.get("result").is_some()
message.payload().id_u64() == Some(command_id) && message.payload().has_field("result")
})
}
@@ -31,9 +30,8 @@ pub(super) fn command_response_succeeded_for_events(
{
return matches!(payload, BackgroundCommandResponsePayloadRef::Success { .. });
}
event.protocol_message().is_some_and(|message| {
message.get("id").and_then(Value::as_u64) == Some(command_id)
&& message.get("result").is_some()
event.protocol_json().is_some_and(|message| {
message.id_u64() == Some(command_id) && message.has_field("result")
})
})
}
@@ -1102,3 +1100,40 @@ pub(super) fn forget_pre_registered_runtime_await(
conn.forget_pending_inspector_await_for_owner(command_id, owner);
}
}
#[cfg(test)]
mod raw_response_tests {
use super::*;
use moli_inspector_json::InspectorJson;
#[test]
fn command_success_checks_use_raw_metadata_without_parsing_the_body() {
for (body, succeeded) in [
(
format!(r#""result":{{"value":"{}"}}"#, "x".repeat(1024 * 1024)),
true,
),
(
format!(r#""result":{}0{}"#, "[".repeat(150), "]".repeat(150)),
true,
),
(
r#""error":{"code":-32603,"message":"\ud800"}"#.to_owned(),
false,
),
] {
let source =
InspectorJson::from_slice(format!(r#"{{"id":7,{body}}}"#).as_bytes()).unwrap();
let messages = [RendererRuntimeInspectorMessage::protocol(source.clone())];
assert_eq!(command_response_succeeded(&messages, Some(7)), succeeded);
assert!(!command_response_succeeded(&messages, Some(8)));
let events = [BackgroundProtocolEvent::immediate(source.clone())];
assert_eq!(
command_response_succeeded_for_events(&events, Some(7)),
succeeded
);
assert!(!command_response_succeeded_for_events(&events, Some(8)));
assert!(!source.is_materialized());
}
}
}
+5 -1
View File
@@ -105,7 +105,11 @@ fn try_start_binding(conn: &mut CdpConnection, cmd: &Cmd<'_>) -> Option<NativeCo
move |reply| match reply {
Ok(RendererPageReply::RuntimeInspectorProtocolMessages(output)) => {
if let Some(error) = output.messages().iter().find_map(|message| match message {
RendererRuntimeInspectorMessage::Protocol(message) => message.get("error"),
RendererRuntimeInspectorMessage::Protocol(message)
if message.payload().has_field("error") =>
{
message.value().get("error")
}
_ => None,
}) {
return RendererNativeProtocolResponse::error(
+16 -16
View File
@@ -324,22 +324,22 @@ pub use runtime::{
RendererRuntimeInspectorIoCommandRoute, RendererRuntimeInspectorMainCommandCompletion,
RendererRuntimeInspectorMainCommandRoute, RendererRuntimeInspectorMessage,
RendererRuntimeInspectorMessageBatch, RendererRuntimeInspectorProtocolMessage,
RendererRuntimeInspectorProtocolMessageValueMut, RendererRuntimeInspectorResponseChannel,
RendererRuntimeInspectorResponseSender, RendererRuntimeObservableSourceItem,
RendererRuntimeObservableSourceSummary, RendererRuntimeRealmInfo, RendererRuntimeRemoteObject,
RendererRuntimeRemoteObjectResolution, RendererScreenshotClip, RendererScreenshotFormat,
RendererScreenshotRegion, RendererScriptExecutionMemoryDiagnostics,
RendererScriptSourceMemoryDiagnostics, RendererScrollIntoViewResult,
RendererServiceWorkerConsoleMessage, RendererServiceWorkerExceptionMessage,
RendererServiceWorkerFetchDiagnostic, RendererServiceWorkerFetchDiagnosticResult,
RendererServiceWorkerMainResourceFetch, RendererServiceWorkerRunIdentity,
RendererServiceWorkerTargetEvent, RendererServiceWorkerTargetInfo,
RendererServiceWorkerVersionStatus, RendererSetDocumentContentResult,
RendererSharedWorkerConsoleMessage, RendererSharedWorkerTargetEvent,
RendererSharedWorkerTargetInfo, RendererStyleSheetHeader, RendererStyleSheetInventoryUpdate,
RendererStyleSheetPayload, RendererSyntheticResponseBody, RendererTextSearchMatch,
RendererTouchPoint, RendererVisualStateToken, RendererWindowDocumentSource,
RuntimeConsoleMessageSnapshot, WorkerInspectorError, renderer_output_transport_channel,
RendererRuntimeInspectorResponseChannel, RendererRuntimeInspectorResponseSender,
RendererRuntimeObservableSourceItem, RendererRuntimeObservableSourceSummary,
RendererRuntimeRealmInfo, RendererRuntimeRemoteObject, RendererRuntimeRemoteObjectResolution,
RendererScreenshotClip, RendererScreenshotFormat, RendererScreenshotRegion,
RendererScriptExecutionMemoryDiagnostics, RendererScriptSourceMemoryDiagnostics,
RendererScrollIntoViewResult, RendererServiceWorkerConsoleMessage,
RendererServiceWorkerExceptionMessage, RendererServiceWorkerFetchDiagnostic,
RendererServiceWorkerFetchDiagnosticResult, RendererServiceWorkerMainResourceFetch,
RendererServiceWorkerRunIdentity, RendererServiceWorkerTargetEvent,
RendererServiceWorkerTargetInfo, RendererServiceWorkerVersionStatus,
RendererSetDocumentContentResult, RendererSharedWorkerConsoleMessage,
RendererSharedWorkerTargetEvent, RendererSharedWorkerTargetInfo, RendererStyleSheetHeader,
RendererStyleSheetInventoryUpdate, RendererStyleSheetPayload, RendererSyntheticResponseBody,
RendererTextSearchMatch, RendererTouchPoint, RendererVisualStateToken,
RendererWindowDocumentSource, RuntimeConsoleMessageSnapshot, WorkerInspectorError,
renderer_output_transport_channel,
};
pub use runtime::{
RendererElementClickError, RendererElementClickTarget, RendererPreparedPointerClick,
+13 -14
View File
@@ -386,20 +386,19 @@ pub use self::page_surface::{
RendererRuntimeCommandOutput, RendererRuntimeEvaluationResult, RendererRuntimeHeapSpaceUsage,
RendererRuntimeHeapUsage, RendererRuntimeInspectorAsyncCompletion,
RendererRuntimeInspectorMessage, RendererRuntimeInspectorMessageBatch,
RendererRuntimeInspectorProtocolMessage, RendererRuntimeInspectorProtocolMessageValueMut,
RendererRuntimeInspectorResponseChannel, RendererRuntimeInspectorResponseSender,
RendererRuntimeObservableSourceItem, RendererRuntimeObservableSourceSummary,
RendererRuntimeRealmInfo, RendererRuntimeRemoteObject, RendererRuntimeRemoteObjectResolution,
RendererScriptExecutionMemoryDiagnostics, RendererScriptSourceMemoryDiagnostics,
RendererScrollIntoViewResult, RendererServiceWorkerConsoleMessage,
RendererServiceWorkerExceptionMessage, RendererServiceWorkerFetchDiagnostic,
RendererServiceWorkerFetchDiagnosticResult, RendererServiceWorkerTargetEvent,
RendererServiceWorkerTargetInfo, RendererServiceWorkerVersionStatus,
RendererSetDocumentContentResult, RendererSharedWorkerConsoleMessage,
RendererSharedWorkerTargetEvent, RendererSharedWorkerTargetInfo, RendererStyleSheetHeader,
RendererStyleSheetInventoryUpdate, RendererStyleSheetPayload, RendererSyntheticResponseBody,
RendererTextSearchMatch, RendererTouchPoint, RendererWindowDocumentSource,
RuntimeConsoleMessageSnapshot,
RendererRuntimeInspectorProtocolMessage, RendererRuntimeInspectorResponseChannel,
RendererRuntimeInspectorResponseSender, RendererRuntimeObservableSourceItem,
RendererRuntimeObservableSourceSummary, RendererRuntimeRealmInfo, RendererRuntimeRemoteObject,
RendererRuntimeRemoteObjectResolution, RendererScriptExecutionMemoryDiagnostics,
RendererScriptSourceMemoryDiagnostics, RendererScrollIntoViewResult,
RendererServiceWorkerConsoleMessage, RendererServiceWorkerExceptionMessage,
RendererServiceWorkerFetchDiagnostic, RendererServiceWorkerFetchDiagnosticResult,
RendererServiceWorkerTargetEvent, RendererServiceWorkerTargetInfo,
RendererServiceWorkerVersionStatus, RendererSetDocumentContentResult,
RendererSharedWorkerConsoleMessage, RendererSharedWorkerTargetEvent,
RendererSharedWorkerTargetInfo, RendererStyleSheetHeader, RendererStyleSheetInventoryUpdate,
RendererStyleSheetPayload, RendererSyntheticResponseBody, RendererTextSearchMatch,
RendererTouchPoint, RendererWindowDocumentSource, RuntimeConsoleMessageSnapshot,
};
pub(crate) use self::page_surface::{
RendererCommandTurnOutputRecorder, RendererDevToolsSessionOutputHost,
+4 -4
View File
@@ -103,7 +103,7 @@ fn renderer_inspector_response_succeeded(
let RendererRuntimeInspectorMessage::Protocol(message) = message else {
return false;
};
message.get("id").and_then(Value::as_u64) == Some(call_id) && message.get("error").is_none()
message.payload().id_u64() == Some(call_id) && !message.payload().has_field("error")
})
}
const DOM_STABLE_COMPLETE_BASE_WINDOW: std::time::Duration = std::time::Duration::from_millis(350);
@@ -119,7 +119,7 @@ fn runtime_inspector_response_message(
let RendererRuntimeInspectorMessage::Protocol(message) = message else {
return None;
};
(message.get("id").and_then(Value::as_u64) == Some(response_id)).then_some(message.value())
(message.payload().id_u64() == Some(response_id)).then(|| message.value())
})
}
@@ -1854,7 +1854,7 @@ impl PageVm {
.filter(|message| {
message.has_v8_inspector_method()
|| matches!(message, RendererRuntimeInspectorMessage::Protocol(message)
if message.get("error").is_some())
if message.payload().has_field("error"))
})
.collect(),
);
@@ -1873,7 +1873,7 @@ impl PageVm {
let output = output.map_err(anyhow::Error::msg)?;
if !output.messages().iter().any(|message| {
matches!(message, RendererRuntimeInspectorMessage::Protocol(message)
if message.get("error").is_some())
if message.payload().has_field("error"))
}) {
let session_key = DevToolsSessionKey::from_wire_session_id(inspector_session_id);
let mut session_bindings = self
+13 -58
View File
@@ -911,12 +911,7 @@ pub enum RendererRuntimeInspectorMessage {
#[derive(Debug, Clone, PartialEq)]
pub struct RendererRuntimeInspectorProtocolMessage {
renderer_call_id: Option<moli_page_types::RendererCallId>,
value: InspectorJson,
}
pub struct RendererRuntimeInspectorProtocolMessageValueMut<'a> {
message: &'a mut RendererRuntimeInspectorProtocolMessage,
payload: InspectorJson,
}
#[derive(Debug, Clone, Default, PartialEq)]
@@ -1026,16 +1021,8 @@ impl RendererRuntimeCommandOutput {
}
pub fn protocol_response(&self, call_id: i32) -> Option<&Value> {
self.messages.iter().find_map(|message| match message {
RendererRuntimeInspectorMessage::Protocol(message)
if message
.renderer_call_id()
.is_some_and(|id| id.get() == call_id) =>
{
Some(message.value())
}
_ => None,
})
self.protocol_response_payload(call_id)
.map(InspectorJson::value)
}
pub fn into_protocol_response(self, call_id: i32) -> Option<Value> {
@@ -1153,70 +1140,38 @@ impl RendererRuntimeInspectorMessage {
}
impl RendererRuntimeInspectorProtocolMessage {
pub fn new(value: impl Into<InspectorJson>) -> Self {
let value = value.into();
let renderer_call_id = renderer_call_id_from_protocol_message(&value);
pub fn new(payload: impl Into<InspectorJson>) -> Self {
Self {
renderer_call_id,
value,
payload: payload.into(),
}
}
pub fn renderer_call_id(&self) -> Option<moli_page_types::RendererCallId> {
self.renderer_call_id
renderer_call_id_from_protocol_message(&self.payload)
}
pub fn value(&self) -> &Value {
self.value.value()
self.payload.value()
}
pub fn payload(&self) -> &InspectorJson {
&self.value
&self.payload
}
pub fn into_payload(self) -> InspectorJson {
self.value
self.payload
}
pub fn set_frontend_id(&mut self, id: u64) {
self.value.set_id(id);
self.renderer_call_id = renderer_call_id_from_protocol_message(&self.value);
self.payload.set_id(id);
}
pub fn value_mut(&mut self) -> RendererRuntimeInspectorProtocolMessageValueMut<'_> {
RendererRuntimeInspectorProtocolMessageValueMut { message: self }
pub fn value_mut(&mut self) -> &mut Value {
self.payload.value_mut()
}
pub fn into_value(self) -> Value {
self.value.into_value()
}
}
impl std::ops::Deref for RendererRuntimeInspectorProtocolMessageValueMut<'_> {
type Target = Value;
fn deref(&self) -> &Self::Target {
self.message.value.value()
}
}
impl std::ops::DerefMut for RendererRuntimeInspectorProtocolMessageValueMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.message.value.value_mut()
}
}
impl Drop for RendererRuntimeInspectorProtocolMessageValueMut<'_> {
fn drop(&mut self) {
self.message.renderer_call_id = renderer_call_id_from_protocol_message(&self.message.value);
}
}
impl std::ops::Deref for RendererRuntimeInspectorProtocolMessage {
type Target = Value;
fn deref(&self) -> &Self::Target {
self.value()
self.payload.into_value()
}
}
@@ -1002,7 +1002,8 @@ async fn runtime_enable_events_include_renderer_root_frame_id_for_default_contex
let default_contexts = events
.iter()
.filter(|message| {
message.get("method") == Some(&serde_json::json!("Runtime.executionContextCreated"))
message.get("method").and_then(serde_json::Value::as_str)
== Some("Runtime.executionContextCreated")
&& message["params"]["context"]["auxData"]["isDefault"] == serde_json::json!(true)
&& message["params"]["context"]["auxData"]["type"] == serde_json::json!("default")
})
@@ -1230,7 +1231,7 @@ async fn inspector_output_flushes_v8_state_for_commands_and_notifications() {
) if batch.messages.iter().any(|message| matches!(
message,
RendererRuntimeInspectorMessage::Protocol(message)
if message.get("id").and_then(serde_json::Value::as_i64) == Some(701)
if message.payload().id_i64() == Some(701)
))
)
})
@@ -1255,8 +1256,7 @@ async fn inspector_output_flushes_v8_state_for_commands_and_notifications() {
&& batch.messages.iter().any(|message| matches!(
message,
RendererRuntimeInspectorMessage::Protocol(message)
if message.get("method")
== Some(&serde_json::json!("Runtime.consoleAPICalled"))
if message.payload().method() == Some("Runtime.consoleAPICalled")
))
))
}),
@@ -1315,8 +1315,7 @@ async fn owner_command_fences_exact_cursor_when_later_output_is_already_publishe
if batch.messages.iter().any(|message| matches!(
message,
RendererRuntimeInspectorMessage::Protocol(message)
if message.get("method")
== Some(&serde_json::json!("Runtime.consoleAPICalled"))
if message.payload().method() == Some("Runtime.consoleAPICalled")
))
)),
"the early publication must own the Runtime notification emitted by the owner command"
@@ -1419,8 +1418,7 @@ async fn page_diagnostics_snapshot_is_read_only_for_current_inspector_output() {
if batch.messages.iter().any(|message| matches!(
message,
RendererRuntimeInspectorMessage::Protocol(message)
if message.get("method")
== Some(&serde_json::json!("Runtime.consoleAPICalled"))
if message.payload().method() == Some("Runtime.consoleAPICalled")
))
))
}),
@@ -4171,9 +4169,9 @@ async fn pending_expression_publishes_console_output_before_settlement() {
let RendererRuntimeInspectorMessage::Protocol(message) = message else {
continue;
};
if message.get("method").and_then(serde_json::Value::as_str)
if message.payload().method()
== Some("Runtime.consoleAPICalled")
&& let Some(value) = message["params"]["args"][0]["value"].as_str()
&& let Some(value) = message.value()["params"]["args"][0]["value"].as_str()
{
observed.push(value.to_owned());
}
@@ -4277,8 +4275,8 @@ async fn navigation_follow_publishes_command_output_before_replacing_document()
if batch.messages.iter().any(|message| matches!(
message,
RendererRuntimeInspectorMessage::Protocol(message)
if message["method"] == "Runtime.consoleAPICalled"
&& message["params"]["args"][0]["value"] == "command-output-before-navigation"
if message.payload().method() == Some("Runtime.consoleAPICalled")
&& message.value()["params"]["args"][0]["value"] == "command-output-before-navigation"
))
))
})
@@ -798,7 +798,7 @@ mod tests {
let restored_context = restored_messages
.iter()
.find(|message| {
message.get("method").and_then(Value::as_str)
message.method()
== Some("Runtime.executionContextCreated")
})
.unwrap_or_else(|| {
@@ -807,7 +807,8 @@ mod tests {
)
});
assert_eq!(
restored_context["params"]["context"]["origin"], "https://example.test",
restored_context.value()["params"]["context"]["origin"],
"https://example.test",
"V8ContextInfo must carry the embedder-bound origin without outbound JSON rewriting"
);
let formatter_probe = serde_json::json!({
@@ -934,19 +935,13 @@ mod tests {
let messages = restored.outbound.values_after(0);
let cleared_index = messages
.iter()
.position(|message| {
message.get("method").and_then(Value::as_str)
== Some("Runtime.executionContextsCleared")
})
.position(|message| message.method() == Some("Runtime.executionContextsCleared"))
.unwrap_or_else(|| {
panic!("restored Runtime session should clear old contexts: {messages:?}")
});
let created_index = messages
.iter()
.position(|message| {
message.get("method").and_then(Value::as_str)
== Some("Runtime.executionContextCreated")
})
.position(|message| message.method() == Some("Runtime.executionContextCreated"))
.unwrap_or_else(|| {
panic!("restored Runtime session should report the new context: {messages:?}")
});
@@ -52,7 +52,7 @@ fn inspector_protocol_message_has_typed_and_consistent_response_id() {
Some(moli_page_types::RendererCallId::new(0))
);
{
let mut value = response.value_mut();
let value = response.value_mut();
value["id"] = json!(-7);
}
assert_eq!(
@@ -249,7 +249,7 @@ fn runtime_command_output_only_parks_its_own_response_callback() {
assert!(matches!(
&current.messages()[0],
crate::runtime::RendererRuntimeInspectorMessage::Protocol(message)
if message["method"] == json!("Runtime.consoleAPICalled")
if message.payload().method() == Some("Runtime.consoleAPICalled")
));
assert_eq!(
current.protocol_response(8),
@@ -549,3 +549,25 @@ fn inspector_outbound_raw_utf16_and_response_id_validation_keep_call_authority()
assert_eq!(outbound.response_callback_counts(), (0, 0));
}
}
#[test]
fn inspector_response_id_reads_opaque_metadata_and_tracks_explicit_mutation() {
let body = format!("{}0{}", "[".repeat(150), "]".repeat(150));
let source =
InspectorJson::from_slice(format!(r#"{{"id":7,"result":{body}}}"#).as_bytes()).unwrap();
let mut message = crate::runtime::RendererRuntimeInspectorProtocolMessage::new(source.clone());
assert_eq!(message.renderer_call_id().map(|id| id.get()), Some(7));
message.set_frontend_id(u64::MAX);
assert_eq!(message.renderer_call_id(), None);
assert_eq!(message.payload().id_u64(), Some(u64::MAX));
message.set_frontend_id(9);
assert_eq!(message.renderer_call_id().map(|id| id.get()), Some(9));
assert!(!source.is_materialized());
assert!(!message.payload().is_materialized());
message.value_mut()["id"] = json!(-7);
assert_eq!(message.renderer_call_id().map(|id| id.get()), Some(-7));
message.value_mut().as_object_mut().unwrap().remove("id");
assert_eq!(message.renderer_call_id(), None);
message.value_mut()["id"] = json!(i64::MAX);
assert_eq!(message.renderer_call_id(), None);
}
@@ -479,7 +479,7 @@ impl ServiceWorkerRuntimeService {
.into_iter()
.partition(|message| match message {
RendererRuntimeInspectorMessage::Protocol(message) => {
message.get("id").is_some()
message.payload().has_field("id")
}
RendererRuntimeInspectorMessage::RuntimeContext(_) => false,
});
@@ -119,7 +119,7 @@ impl RendererSharedWorkerHost {
.into_iter()
.partition(|message| match message {
RendererRuntimeInspectorMessage::Protocol(message) => {
message.get("id").is_some()
message.payload().has_field("id")
}
RendererRuntimeInspectorMessage::RuntimeContext(_) => false,
});
@@ -942,7 +942,7 @@ mod tests {
.iter()
.find_map(|message| match message {
RendererRuntimeInspectorMessage::Protocol(message)
if message.value().get("id").and_then(Value::as_i64) == Some(call_id) =>
if message.payload().id_i64() == Some(call_id) =>
{
Some(message.value().clone())
}