mirror of
https://github.com/lexmount/moli.git
synced 2026-09-23 16:01:25 +00:00
fix(emulation): reject unavailable throttling modes
This commit is contained in:
@@ -356,6 +356,11 @@ Its current intentional boundaries include:
|
||||
paint architecture.
|
||||
- It does not pursue pixel-for-pixel parity with Chrome or provide
|
||||
high-fidelity Canvas/WebGL/media playback.
|
||||
- Emulation supports offline mode, but CPU throttling, network latency/throughput,
|
||||
packet shaping, and connection type overrides return a protocol error. CPU rates
|
||||
at or below 1 disable throttling; non-positive latency and throughput values
|
||||
disable network throttling. Unsupported combinations are rejected before
|
||||
changing offline state.
|
||||
- `--layout` supports software screenshots and raster-backed CDP PDF
|
||||
generation, but not every Chrome screenshot or print mode is implemented.
|
||||
|
||||
|
||||
@@ -263,6 +263,7 @@ Kitesurf 是远程服务,无法测量 CPU、内存和进程数,因此资源
|
||||
|
||||
- 不提供 GUI 浏览器、持久化窗口、GPU 合成器,也不实现保留式的多帧绘制架构。
|
||||
- 不追求与 Chrome 像素级一致的渲染效果,也不提供高保真的 Canvas/WebGL/媒体播放能力。
|
||||
- 环境模拟支持 offline 切换;CPU 节流、网络延迟/吞吐、丢包及连接类型覆盖暂不支持,请求会返回协议错误。CPU rate ≤ 1 和非正的网络延迟/吞吐值用于取消节流;不支持的组合会在改变 offline 状态前整体拒绝。
|
||||
- 在 `--layout` 模式下支持软件截图和基于光栅化的 CDP PDF 生成,但没有实现 Chrome 的全部截图/打印模式。
|
||||
|
||||
遇到不支持的协议路径,Moli 会直接明确报错——它不会假装某个浏览器操作、事件、网络观测或者视觉结果已经发生。
|
||||
|
||||
@@ -17,21 +17,9 @@ impl TargetSessionStateMut<'_> {
|
||||
true
|
||||
}
|
||||
|
||||
fn set_emulated_network_conditions(
|
||||
mut self,
|
||||
offline: bool,
|
||||
latency: f64,
|
||||
download_throughput: f64,
|
||||
upload_throughput: f64,
|
||||
connection_type: Option<String>,
|
||||
) -> bool {
|
||||
self.network_policy_mut().set_emulated_network_conditions(
|
||||
offline,
|
||||
latency,
|
||||
download_throughput,
|
||||
upload_throughput,
|
||||
connection_type,
|
||||
)
|
||||
fn set_network_offline(mut self, offline: bool) -> bool {
|
||||
self.network_policy_mut().set_network_offline(offline);
|
||||
offline
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,23 +277,12 @@ impl TargetSessionOwnerMut<'_> {
|
||||
self.mutate_session_state_ref(|state| state.set_tls_verify_host_override(enabled))
|
||||
}
|
||||
|
||||
fn start_set_emulated_network_conditions(
|
||||
fn start_set_network_offline(
|
||||
mut self,
|
||||
offline: bool,
|
||||
latency: f64,
|
||||
download_throughput: f64,
|
||||
upload_throughput: f64,
|
||||
connection_type: Option<String>,
|
||||
) -> Result<Option<PendingPageCommand>, String> {
|
||||
let effective_offline = self.mutate_session_state_ref(|state| {
|
||||
state.set_emulated_network_conditions(
|
||||
offline,
|
||||
latency,
|
||||
download_throughput,
|
||||
upload_throughput,
|
||||
connection_type,
|
||||
)
|
||||
});
|
||||
let effective_offline =
|
||||
self.mutate_session_state_ref(|state| state.set_network_offline(offline));
|
||||
let Some(page) = self.runtime_slot_mut().loaded_page_mut() else {
|
||||
return Ok(None);
|
||||
};
|
||||
@@ -818,45 +795,24 @@ impl CdpConnection {
|
||||
self.start_rebuild_resource_runtime_for_session_owner(session_id)
|
||||
}
|
||||
|
||||
pub(crate) fn start_set_emulated_network_conditions_for_session_owner(
|
||||
pub(crate) fn start_set_network_offline_for_session_owner(
|
||||
&mut self,
|
||||
session_id: Option<&str>,
|
||||
offline: bool,
|
||||
latency: f64,
|
||||
download_throughput: f64,
|
||||
upload_throughput: f64,
|
||||
connection_type: Option<String>,
|
||||
) -> Result<Option<PendingPageCommand>, String> {
|
||||
let owner = crate::conn::CommandOwnerScope::capture(self, session_id);
|
||||
self.start_set_emulated_network_conditions_for_owner(
|
||||
&owner,
|
||||
offline,
|
||||
latency,
|
||||
download_throughput,
|
||||
upload_throughput,
|
||||
connection_type,
|
||||
)
|
||||
self.start_set_network_offline_for_owner(&owner, offline)
|
||||
}
|
||||
|
||||
pub(crate) fn start_set_emulated_network_conditions_for_owner(
|
||||
pub(crate) fn start_set_network_offline_for_owner(
|
||||
&mut self,
|
||||
command_owner: &crate::conn::CommandOwnerScope,
|
||||
offline: bool,
|
||||
latency: f64,
|
||||
download_throughput: f64,
|
||||
upload_throughput: f64,
|
||||
connection_type: Option<String>,
|
||||
) -> Result<Option<PendingPageCommand>, String> {
|
||||
let Some(owner) = self.target_session_owner_mut_for_owner(command_owner) else {
|
||||
return Err("BrowserContextNotLoaded".to_owned());
|
||||
};
|
||||
owner.start_set_emulated_network_conditions(
|
||||
offline,
|
||||
latency,
|
||||
download_throughput,
|
||||
upload_throughput,
|
||||
connection_type,
|
||||
)
|
||||
owner.start_set_network_offline(offline)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -958,13 +914,7 @@ mod tests {
|
||||
network.blocked_url_patterns = vec!["*://blocked.test/*".to_owned()];
|
||||
network.extra_headers = vec![("X-Test".to_owned(), "active".to_owned())];
|
||||
}
|
||||
let active_offline = active_session_state_mut(&mut active).set_emulated_network_conditions(
|
||||
true,
|
||||
25.0,
|
||||
1024.0,
|
||||
256.0,
|
||||
Some("cellular3g".to_owned()),
|
||||
);
|
||||
let active_offline = active_session_state_mut(&mut active).set_network_offline(true);
|
||||
|
||||
assert!(
|
||||
active
|
||||
@@ -994,34 +944,6 @@ mod tests {
|
||||
);
|
||||
assert!(active_offline);
|
||||
assert!(active.active_page_target().network_policy.network_offline());
|
||||
assert_eq!(
|
||||
active
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_network_latency(),
|
||||
25.0
|
||||
);
|
||||
assert_eq!(
|
||||
active
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_download_throughput(),
|
||||
1024.0
|
||||
);
|
||||
assert_eq!(
|
||||
active
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_upload_throughput(),
|
||||
256.0
|
||||
);
|
||||
assert_eq!(
|
||||
active
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_connection_type(),
|
||||
Some("cellular3g")
|
||||
);
|
||||
|
||||
let mut background = PageTargetHost::empty("TID-network-owner-test".to_owned());
|
||||
{
|
||||
@@ -1035,14 +957,8 @@ mod tests {
|
||||
network.blocked_url_patterns = vec!["*://background-blocked.test/*".to_owned()];
|
||||
network.extra_headers = vec![("X-Test".to_owned(), "background".to_owned())];
|
||||
}
|
||||
let background_offline = background_session_state_mut(&mut background)
|
||||
.set_emulated_network_conditions(
|
||||
true,
|
||||
50.0,
|
||||
2048.0,
|
||||
512.0,
|
||||
Some("cellular4g".to_owned()),
|
||||
);
|
||||
let background_offline =
|
||||
background_session_state_mut(&mut background).set_network_offline(true);
|
||||
|
||||
assert!(background.effective_policy().cache_disabled());
|
||||
assert!(background.effective_policy().bypass_service_worker());
|
||||
@@ -1056,19 +972,6 @@ mod tests {
|
||||
);
|
||||
assert!(background_offline);
|
||||
assert!(background.network_policy.network_offline());
|
||||
assert_eq!(background.network_policy.emulated_network_latency(), 50.0);
|
||||
assert_eq!(
|
||||
background.network_policy.emulated_download_throughput(),
|
||||
2048.0
|
||||
);
|
||||
assert_eq!(
|
||||
background.network_policy.emulated_upload_throughput(),
|
||||
512.0
|
||||
);
|
||||
assert_eq!(
|
||||
background.network_policy.emulated_connection_type(),
|
||||
Some("cellular4g")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -8000,10 +8000,9 @@ async fn command_dispatch_completes_live_network_emulation_without_legacy_fallba
|
||||
"method": "Network.emulateNetworkConditions",
|
||||
"params": {
|
||||
"offline": true,
|
||||
"latency": 20,
|
||||
"downloadThroughput": 1024,
|
||||
"uploadThroughput": 512,
|
||||
"connectionType": "cellular3g"
|
||||
"latency": 0,
|
||||
"downloadThroughput": -1,
|
||||
"uploadThroughput": -1,
|
||||
}
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
@@ -538,35 +538,16 @@ pub(crate) struct InspectorSessionState {
|
||||
pub(crate) v8_state: Option<V8InspectorSessionState>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub(crate) struct TargetNetworkPolicyState {
|
||||
// Target-wide policy contributed by WebDriver BiDi or connection defaults.
|
||||
base_cache_disabled: bool,
|
||||
network_offline: bool,
|
||||
emulated_network_latency: f64,
|
||||
emulated_download_throughput: f64,
|
||||
emulated_upload_throughput: f64,
|
||||
emulated_connection_type: Option<String>,
|
||||
base_browser_identity: super::BaseBrowserIdentityOverrideState,
|
||||
// Target-scoped headers contributed by WebDriver BiDi.
|
||||
base_extra_headers: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
impl Default for TargetNetworkPolicyState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
base_cache_disabled: false,
|
||||
network_offline: false,
|
||||
emulated_network_latency: 0.0,
|
||||
emulated_download_throughput: -1.0,
|
||||
emulated_upload_throughput: -1.0,
|
||||
emulated_connection_type: None,
|
||||
base_browser_identity: super::BaseBrowserIdentityOverrideState::default(),
|
||||
base_extra_headers: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TargetNetworkPolicyState {
|
||||
pub(crate) fn set_base_cache_disabled(&mut self, cache_disabled: bool) {
|
||||
self.base_cache_disabled = cache_disabled;
|
||||
@@ -586,7 +567,6 @@ impl TargetNetworkPolicyState {
|
||||
self.network_offline
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn set_network_offline(&mut self, network_offline: bool) {
|
||||
self.network_offline = network_offline;
|
||||
}
|
||||
@@ -638,42 +618,6 @@ impl TargetNetworkPolicyState {
|
||||
self.base_browser_identity
|
||||
.set_accept_language(accept_language, fallback);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn emulated_network_latency(&self) -> f64 {
|
||||
self.emulated_network_latency
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn emulated_download_throughput(&self) -> f64 {
|
||||
self.emulated_download_throughput
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn emulated_upload_throughput(&self) -> f64 {
|
||||
self.emulated_upload_throughput
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn emulated_connection_type(&self) -> Option<&str> {
|
||||
self.emulated_connection_type.as_deref()
|
||||
}
|
||||
|
||||
pub(crate) fn set_emulated_network_conditions(
|
||||
&mut self,
|
||||
offline: bool,
|
||||
latency: f64,
|
||||
download_throughput: f64,
|
||||
upload_throughput: f64,
|
||||
connection_type: Option<String>,
|
||||
) -> bool {
|
||||
self.network_offline = offline;
|
||||
self.emulated_network_latency = latency;
|
||||
self.emulated_download_throughput = download_throughput;
|
||||
self.emulated_upload_throughput = upload_throughput;
|
||||
self.emulated_connection_type = connection_type;
|
||||
self.network_offline
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -3298,7 +3298,7 @@ async fn direct_network_policy_routes_to_inactive_active_owner_without_activatin
|
||||
r#"{"id":3,"method":"Network.setBlockedURLs","sessionId":"SID-B","params":{"urls":["*://blocked.test/*"]}}"#,
|
||||
r#"{"id":4,"method":"Network.setExtraHTTPHeaders","sessionId":"SID-B","params":{"headers":{"X-Test":"direct"}}}"#,
|
||||
r#"{"id":5,"method":"Network.setUserAgentOverride","sessionId":"SID-B","params":{"userAgent":"Moli/Direct-UA"}}"#,
|
||||
r#"{"id":6,"method":"Network.emulateNetworkConditions","sessionId":"SID-B","params":{"offline":true,"latency":25,"downloadThroughput":1024,"uploadThroughput":256,"connectionType":"cellular3g"}}"#,
|
||||
r#"{"id":6,"method":"Network.emulateNetworkConditions","sessionId":"SID-B","params":{"offline":true,"latency": 0,"downloadThroughput": -1,"uploadThroughput": -1}}"#,
|
||||
] {
|
||||
let response = conn.process_message_messages_only_for_test(raw).await;
|
||||
let request_id = serde_json::from_str::<serde_json::Value>(raw)
|
||||
@@ -3361,34 +3361,6 @@ async fn direct_network_policy_routes_to_inactive_active_owner_without_activatin
|
||||
.network_policy
|
||||
.network_offline()
|
||||
);
|
||||
assert_eq!(
|
||||
inactive
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_network_latency(),
|
||||
25.0
|
||||
);
|
||||
assert_eq!(
|
||||
inactive
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_download_throughput(),
|
||||
1024.0
|
||||
);
|
||||
assert_eq!(
|
||||
inactive
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_upload_throughput(),
|
||||
256.0
|
||||
);
|
||||
assert_eq!(
|
||||
inactive
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_connection_type(),
|
||||
Some("cellular3g")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -3465,7 +3437,7 @@ async fn direct_network_policy_routes_to_inactive_background_owner_without_activ
|
||||
r#"{"id":3,"method":"Network.setBlockedURLs","sessionId":"SID-background","params":{"urls":["*://blocked-background.test/*"]}}"#,
|
||||
r#"{"id":4,"method":"Network.setExtraHTTPHeaders","sessionId":"SID-background","params":{"headers":{"X-Background":"direct"}}}"#,
|
||||
r#"{"id":5,"method":"Network.setUserAgentOverride","sessionId":"SID-background","params":{"userAgent":"Moli/Background-UA"}}"#,
|
||||
r#"{"id":6,"method":"Network.emulateNetworkConditions","sessionId":"SID-background","params":{"offline":true,"latency":50,"downloadThroughput":2048,"uploadThroughput":512,"connectionType":"wifi"}}"#,
|
||||
r#"{"id":6,"method":"Network.emulateNetworkConditions","sessionId":"SID-background","params":{"offline":true,"latency": 0,"downloadThroughput": -1,"uploadThroughput": -1}}"#,
|
||||
] {
|
||||
let response = conn.process_message_messages_only_for_test(raw).await;
|
||||
let request_id = serde_json::from_str::<serde_json::Value>(raw)
|
||||
@@ -3510,13 +3482,6 @@ async fn direct_network_policy_routes_to_inactive_background_owner_without_activ
|
||||
Some("Moli/Background-UA")
|
||||
);
|
||||
assert!(staged.network_policy.network_offline());
|
||||
assert_eq!(staged.network_policy.emulated_network_latency(), 50.0);
|
||||
assert_eq!(staged.network_policy.emulated_download_throughput(), 2048.0);
|
||||
assert_eq!(staged.network_policy.emulated_upload_throughput(), 512.0);
|
||||
assert_eq!(
|
||||
staged.network_policy.emulated_connection_type(),
|
||||
Some("wifi")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -361,12 +361,18 @@ fn start_cpu_throttling_rate_command(
|
||||
));
|
||||
}
|
||||
};
|
||||
if params.rate > 1.0 {
|
||||
return EmulationCommandTaskStep::Complete(CommandOutputPlan::error(
|
||||
-32000,
|
||||
"CPU throttling is not supported",
|
||||
));
|
||||
}
|
||||
if conn.browser_context.is_none() {
|
||||
return EmulationCommandTaskStep::Complete(CommandOutputPlan::result(json!({})));
|
||||
}
|
||||
if !conn.update_emulation_state_for_session_owner(cmd.session_id, |state| {
|
||||
if let Some(mut state) = state {
|
||||
state.set_cpu_throttling_rate(params.rate);
|
||||
state.set_cpu_throttling_rate(1.0);
|
||||
}
|
||||
}) {
|
||||
return EmulationCommandTaskStep::Complete(CommandOutputPlan::error(
|
||||
@@ -378,7 +384,7 @@ fn start_cpu_throttling_rate_command(
|
||||
let Some(page) = loaded_page_mut_for_target_configuration(conn, cmd.session_id) else {
|
||||
return EmulationCommandTaskStep::Complete(CommandOutputPlan::result(json!({})));
|
||||
};
|
||||
match page.start_set_cpu_throttling_rate(params.rate) {
|
||||
match page.start_set_cpu_throttling_rate(1.0) {
|
||||
Ok(pending) => EmulationCommandTaskStep::Pending(single_pending_emulation_dispatch(
|
||||
cmd.id,
|
||||
owner_scope,
|
||||
@@ -1389,14 +1395,7 @@ fn start_network_conditions_update_for_current_route(
|
||||
};
|
||||
let owner = CommandOwnerScope::for_route(route.clone());
|
||||
let network_update = conn
|
||||
.start_set_emulated_network_conditions_for_owner(
|
||||
&owner,
|
||||
effective_offline,
|
||||
0.0,
|
||||
-1.0,
|
||||
-1.0,
|
||||
None,
|
||||
)
|
||||
.start_set_network_offline_for_owner(&owner, effective_offline)
|
||||
.map_err(devtools_emulation_owner_error)?;
|
||||
let mut pending = Vec::new();
|
||||
if let Some(network_update) = network_update {
|
||||
|
||||
@@ -935,11 +935,7 @@ async fn live_apply_emulation_commands_without_loaded_page_do_not_use_legacy_fal
|
||||
"Emulation.setGeolocationOverride",
|
||||
json!({ "latitude": 48.85837, "longitude": 2.294481, "accuracy": 7 }),
|
||||
),
|
||||
(
|
||||
9128,
|
||||
"Emulation.setCPUThrottlingRate",
|
||||
json!({ "rate": 2.5 }),
|
||||
),
|
||||
(9128, "Emulation.setCPUThrottlingRate", json!({ "rate": 1 })),
|
||||
] {
|
||||
let raw = json!({
|
||||
"id": id,
|
||||
@@ -967,7 +963,7 @@ async fn live_apply_emulation_commands_without_loaded_page_do_not_use_legacy_fal
|
||||
.active_page_target()
|
||||
.effective_emulation_state
|
||||
.cpu_throttling_rate,
|
||||
2.5
|
||||
1.0
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1069,7 +1065,7 @@ async fn device_metrics_completion_survives_initial_page_replacement() {
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn live_cpu_throttling_rate_uses_pending_command_dispatch() {
|
||||
async fn clearing_cpu_throttling_uses_pending_command_dispatch() {
|
||||
let mut ctx = TestContext::new();
|
||||
load_session_page_for_pending_emulation_test(&mut ctx).await;
|
||||
|
||||
@@ -1077,7 +1073,7 @@ async fn live_cpu_throttling_rate_uses_pending_command_dispatch() {
|
||||
"id": 9129,
|
||||
"sessionId": "SID-1",
|
||||
"method": "Emulation.setCPUThrottlingRate",
|
||||
"params": { "rate": 3.0 }
|
||||
"params": { "rate": 1 }
|
||||
})
|
||||
.to_string();
|
||||
let CdpCommandTaskStep::Pending(pending) = ctx.conn.start_command_dispatch(&raw) else {
|
||||
@@ -1103,7 +1099,7 @@ async fn live_cpu_throttling_rate_uses_pending_command_dispatch() {
|
||||
.active_page_target()
|
||||
.effective_emulation_state
|
||||
.cpu_throttling_rate,
|
||||
3.0
|
||||
1.0
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1385,7 +1381,7 @@ async fn multi_session_emulation_separates_handler_input_from_target_effective_s
|
||||
71_101,
|
||||
"SID-primary",
|
||||
"Emulation.setCPUThrottlingRate",
|
||||
json!({ "rate": 4 }),
|
||||
json!({ "rate": 1 }),
|
||||
)
|
||||
.await;
|
||||
expect_session_command_result(
|
||||
@@ -1393,7 +1389,7 @@ async fn multi_session_emulation_separates_handler_input_from_target_effective_s
|
||||
71_102,
|
||||
"SID-attached",
|
||||
"Emulation.setCPUThrottlingRate",
|
||||
json!({ "rate": 2 }),
|
||||
json!({ "rate": 1 }),
|
||||
)
|
||||
.await;
|
||||
expect_session_command_result(
|
||||
@@ -1439,8 +1435,8 @@ async fn multi_session_emulation_separates_handler_input_from_target_effective_s
|
||||
.conn
|
||||
.emulation_session_state_for_session_owner(Some("SID-attached"))
|
||||
.expect("attached Emulation handler state");
|
||||
assert_eq!(primary.cpu_throttling_rate, 4.0);
|
||||
assert_eq!(attached.cpu_throttling_rate, 2.0);
|
||||
assert_eq!(primary.cpu_throttling_rate, 1.0);
|
||||
assert_eq!(attached.cpu_throttling_rate, 1.0);
|
||||
assert_eq!(
|
||||
primary
|
||||
.emulated_device_metrics
|
||||
@@ -1464,7 +1460,7 @@ async fn multi_session_emulation_separates_handler_input_from_target_effective_s
|
||||
.as_ref()
|
||||
.expect("browser context")
|
||||
.active_page_target();
|
||||
assert_eq!(target.effective_emulation_state.cpu_throttling_rate, 2.0);
|
||||
assert_eq!(target.effective_emulation_state.cpu_throttling_rate, 1.0);
|
||||
assert_eq!(
|
||||
target
|
||||
.effective_emulation_state
|
||||
@@ -1499,7 +1495,7 @@ async fn multi_session_emulation_separates_handler_input_from_target_effective_s
|
||||
.conn
|
||||
.emulation_session_state_for_session_owner(Some("SID-primary"))
|
||||
.expect("primary Emulation handler state survives attached disposal");
|
||||
assert_eq!(primary.cpu_throttling_rate, 4.0);
|
||||
assert_eq!(primary.cpu_throttling_rate, 1.0);
|
||||
assert!(primary.emulated_device_metrics.is_some());
|
||||
assert!(primary.focus_emulation_enabled);
|
||||
assert_eq!(
|
||||
@@ -3465,7 +3461,7 @@ async fn target_session_detach_disposes_non_aggregated_emulation_state_before_re
|
||||
"id": 1901,
|
||||
"method": "Emulation.setCPUThrottlingRate",
|
||||
"sessionId": session_id,
|
||||
"params": { "rate": 4 }
|
||||
"params": { "rate": 1 }
|
||||
}))
|
||||
.await;
|
||||
ctx.expect_result(1901, json!({}), Some(&session_id));
|
||||
|
||||
@@ -410,3 +410,65 @@ async fn device_metrics_zero_axes_use_visible_size_and_clear_restores_native_def
|
||||
.await;
|
||||
assert_eq!(evaluate(&mut ctx, snapshot).await, baseline);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn unsupported_throttling_rejects_before_changing_live_offline_state() {
|
||||
let mut ctx = setup().await;
|
||||
for offline in [false, true] {
|
||||
expect_session_command_result(
|
||||
&mut ctx,
|
||||
88001,
|
||||
"SID-1",
|
||||
"Network.emulateNetworkConditions",
|
||||
json!({"offline":offline,"latency":0,"downloadThroughput":-1,"uploadThroughput":0}),
|
||||
)
|
||||
.await;
|
||||
for unsupported in [
|
||||
json!({"latency":100}),
|
||||
json!({"downloadThroughput":1024}),
|
||||
json!({"uploadThroughput":1024}),
|
||||
json!({"connectionType":"cellular3g"}),
|
||||
json!({"packetLoss":1}),
|
||||
json!({"packetQueueLength":1}),
|
||||
json!({"packetReordering":true}),
|
||||
] {
|
||||
let mut params = json!({"offline": !offline,"latency":0,"downloadThroughput":-1,"uploadThroughput":-1});
|
||||
params
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.extend(unsupported.as_object().unwrap().clone());
|
||||
ctx.process_async(json!({"id":88001,"sessionId":"SID-1","method":"Network.emulateNetworkConditions","params":params})).await;
|
||||
ctx.expect_error(
|
||||
88001,
|
||||
-32000,
|
||||
"Network throttling and connection type overrides are not supported",
|
||||
);
|
||||
assert_eq!(
|
||||
evaluate(&mut ctx, "navigator.onLine").await,
|
||||
json!(!offline)
|
||||
);
|
||||
}
|
||||
}
|
||||
for rate in [-1.0, 0.0, 0.5, 1.0] {
|
||||
expect_session_command_result(
|
||||
&mut ctx,
|
||||
88001,
|
||||
"SID-1",
|
||||
"Emulation.setCPUThrottlingRate",
|
||||
json!({"rate":rate}),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ctx.process_async(json!({"id":88001,"sessionId":"SID-1","method":"Emulation.setCPUThrottlingRate","params":{"rate":4}})).await;
|
||||
ctx.expect_error(88001, -32000, "CPU throttling is not supported");
|
||||
assert_eq!(
|
||||
ctx.conn
|
||||
.browser_context
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.active_page_target()
|
||||
.effective_emulation_state
|
||||
.cpu_throttling_rate,
|
||||
1.0
|
||||
);
|
||||
}
|
||||
|
||||
@@ -610,8 +610,8 @@ fn start_emulate_network_conditions_command(
|
||||
conn: &mut CdpConnection,
|
||||
cmd: &Cmd<'_>,
|
||||
) -> NetworkCommandTaskStep {
|
||||
let conditions = match settings::emulated_network_conditions_for_command(cmd) {
|
||||
Ok(conditions) => conditions,
|
||||
let offline = match settings::network_offline_for_emulation_command(cmd) {
|
||||
Ok(offline) => offline,
|
||||
Err(plan) => return NetworkCommandTaskStep::Complete(plan),
|
||||
};
|
||||
pending_network_page_command_step(
|
||||
@@ -619,16 +619,7 @@ fn start_emulate_network_conditions_command(
|
||||
cmd.id,
|
||||
cmd.session_id,
|
||||
PendingNetworkCommandKind::EmulateNetworkConditions,
|
||||
|conn| {
|
||||
conn.start_set_emulated_network_conditions_for_session_owner(
|
||||
cmd.session_id,
|
||||
conditions.offline,
|
||||
conditions.latency,
|
||||
conditions.download_throughput,
|
||||
conditions.upload_throughput,
|
||||
conditions.connection_type,
|
||||
)
|
||||
},
|
||||
|conn| conn.start_set_network_offline_for_session_owner(cmd.session_id, offline),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,6 @@ struct SetBlockedUrlsParams {
|
||||
urls: Vec<String>,
|
||||
}
|
||||
|
||||
pub(super) struct EmulatedNetworkConditionsForCommand {
|
||||
pub(super) offline: bool,
|
||||
pub(super) latency: f64,
|
||||
pub(super) download_throughput: f64,
|
||||
pub(super) upload_throughput: f64,
|
||||
pub(super) connection_type: Option<String>,
|
||||
}
|
||||
|
||||
pub(super) fn enabled_command_output_plan(
|
||||
conn: &mut CdpConnection,
|
||||
session_id: Option<&str>,
|
||||
@@ -82,24 +74,34 @@ pub(super) fn blocked_urls_for_command(cmd: &Cmd<'_>) -> Result<Vec<String>, Com
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub(super) fn emulated_network_conditions_for_command(
|
||||
pub(super) fn network_offline_for_emulation_command(
|
||||
cmd: &Cmd<'_>,
|
||||
) -> Result<EmulatedNetworkConditionsForCommand, CommandOutputPlan> {
|
||||
) -> Result<bool, CommandOutputPlan> {
|
||||
let params: EmulateNetworkConditionsParams = match cmd.get_params() {
|
||||
Ok(Some(params)) => params,
|
||||
_ => return Err(CommandOutputPlan::error(-32602, "InvalidParams")),
|
||||
};
|
||||
let connection_type = params
|
||||
.connection_type
|
||||
.as_ref()
|
||||
.and_then(cdp_connection_type_string);
|
||||
Ok(EmulatedNetworkConditionsForCommand {
|
||||
offline: params.offline,
|
||||
latency: params.latency,
|
||||
download_throughput: params.download_throughput,
|
||||
upload_throughput: params.upload_throughput,
|
||||
connection_type,
|
||||
})
|
||||
if !params.latency.is_finite()
|
||||
|| !params.download_throughput.is_finite()
|
||||
|| !params.upload_throughput.is_finite()
|
||||
|| params.packet_loss.is_some_and(|value| !value.is_finite())
|
||||
{
|
||||
return Err(CommandOutputPlan::error(-32602, "InvalidParams"));
|
||||
}
|
||||
if params.latency > 0.0
|
||||
|| params.download_throughput > 0.0
|
||||
|| params.upload_throughput > 0.0
|
||||
|| params.connection_type.is_some()
|
||||
|| params.packet_loss.is_some_and(|value| value != 0.0)
|
||||
|| params.packet_queue_length.is_some_and(|value| value != 0)
|
||||
|| params.packet_reordering == Some(true)
|
||||
{
|
||||
return Err(CommandOutputPlan::error(
|
||||
-32000,
|
||||
"Network throttling and connection type overrides are not supported",
|
||||
));
|
||||
}
|
||||
Ok(params.offline)
|
||||
}
|
||||
|
||||
pub(super) fn extra_http_headers_for_command(
|
||||
@@ -251,11 +253,3 @@ fn extra_http_headers_from_params(
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
}
|
||||
|
||||
fn cdp_connection_type_string(
|
||||
connection_type: &chromiumoxide_cdp::cdp::browser_protocol::network::ConnectionType,
|
||||
) -> Option<String> {
|
||||
serde_json::to_value(connection_type)
|
||||
.ok()
|
||||
.and_then(|value| value.as_str().map(ToOwned::to_owned))
|
||||
}
|
||||
|
||||
@@ -1307,10 +1307,9 @@ async fn emulate_network_conditions_updates_browser_context_state() {
|
||||
"method": "Network.emulateNetworkConditions",
|
||||
"params": {
|
||||
"offline": true,
|
||||
"latency": 150,
|
||||
"downloadThroughput": 1024,
|
||||
"uploadThroughput": 512,
|
||||
"connectionType": "cellular3g"
|
||||
"latency": 0,
|
||||
"downloadThroughput": -1,
|
||||
"uploadThroughput": -1,
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
@@ -1318,30 +1317,6 @@ async fn emulate_network_conditions_updates_browser_context_state() {
|
||||
|
||||
let bc = ctx.conn.browser_context.as_ref().unwrap();
|
||||
assert!(bc.active_page_target().network_policy.network_offline());
|
||||
assert_eq!(
|
||||
bc.active_page_target()
|
||||
.network_policy
|
||||
.emulated_network_latency(),
|
||||
150.0
|
||||
);
|
||||
assert_eq!(
|
||||
bc.active_page_target()
|
||||
.network_policy
|
||||
.emulated_download_throughput(),
|
||||
1024.0
|
||||
);
|
||||
assert_eq!(
|
||||
bc.active_page_target()
|
||||
.network_policy
|
||||
.emulated_upload_throughput(),
|
||||
512.0
|
||||
);
|
||||
assert_eq!(
|
||||
bc.active_page_target()
|
||||
.network_policy
|
||||
.emulated_connection_type(),
|
||||
Some("cellular3g")
|
||||
);
|
||||
}
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn emulate_network_conditions_offline_navigation_fails_before_completion_events() {
|
||||
|
||||
@@ -1554,10 +1554,9 @@ async fn same_context_background_session_can_stage_its_own_network_conditions_be
|
||||
"sessionId": "SID-active",
|
||||
"params": {
|
||||
"offline": false,
|
||||
"latency": 10,
|
||||
"downloadThroughput": 4096,
|
||||
"uploadThroughput": 2048,
|
||||
"connectionType": "wifi"
|
||||
"latency": 0,
|
||||
"downloadThroughput": -1,
|
||||
"uploadThroughput": -1,
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
@@ -1590,10 +1589,9 @@ async fn same_context_background_session_can_stage_its_own_network_conditions_be
|
||||
"sessionId": second_session_id,
|
||||
"params": {
|
||||
"offline": true,
|
||||
"latency": 25,
|
||||
"downloadThroughput": 1024,
|
||||
"uploadThroughput": 256,
|
||||
"connectionType": "cellular3g"
|
||||
"latency": 0,
|
||||
"downloadThroughput": -1,
|
||||
"uploadThroughput": -1,
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
@@ -1607,25 +1605,12 @@ async fn same_context_background_session_can_stage_its_own_network_conditions_be
|
||||
.expect("active browser context");
|
||||
assert_eq!(active.active_target_id(), Some("TID-000000000PN"));
|
||||
assert!(!active.active_page_target().network_policy.network_offline());
|
||||
assert_eq!(
|
||||
active
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_network_latency(),
|
||||
10.0
|
||||
);
|
||||
|
||||
let staged = active
|
||||
.background_target(&second_target_id)
|
||||
.filter(|target| target.has_non_default_session_state())
|
||||
.expect("second target should have staged background page session state");
|
||||
assert!(staged.network_policy.network_offline());
|
||||
assert_eq!(staged.network_policy.emulated_network_latency(), 25.0);
|
||||
assert_eq!(staged.network_policy.emulated_download_throughput(), 1024.0);
|
||||
assert_eq!(staged.network_policy.emulated_upload_throughput(), 256.0);
|
||||
assert_eq!(
|
||||
staged.network_policy.emulated_connection_type(),
|
||||
Some("cellular3g")
|
||||
);
|
||||
}
|
||||
|
||||
ctx.process_async(json!({
|
||||
@@ -1673,34 +1658,6 @@ async fn same_context_background_session_can_stage_its_own_network_conditions_be
|
||||
.network_policy
|
||||
.network_offline()
|
||||
);
|
||||
assert_eq!(
|
||||
activated
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_network_latency(),
|
||||
25.0
|
||||
);
|
||||
assert_eq!(
|
||||
activated
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_download_throughput(),
|
||||
1024.0
|
||||
);
|
||||
assert_eq!(
|
||||
activated
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_upload_throughput(),
|
||||
256.0
|
||||
);
|
||||
assert_eq!(
|
||||
activated
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_connection_type(),
|
||||
Some("cellular3g")
|
||||
);
|
||||
}
|
||||
|
||||
ctx.process_async(json!({
|
||||
@@ -1921,10 +1878,9 @@ async fn same_context_background_session_can_reset_its_own_network_conditions_be
|
||||
"sessionId": second_session_id,
|
||||
"params": {
|
||||
"offline": true,
|
||||
"latency": 250,
|
||||
"downloadThroughput": 1024,
|
||||
"uploadThroughput": 512,
|
||||
"connectionType": "cellular3g"
|
||||
"latency": 0,
|
||||
"downloadThroughput": -1,
|
||||
"uploadThroughput": -1,
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
@@ -1939,7 +1895,6 @@ async fn same_context_background_session_can_reset_its_own_network_conditions_be
|
||||
"latency": 0,
|
||||
"downloadThroughput": -1,
|
||||
"uploadThroughput": -1,
|
||||
"connectionType": "none"
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
@@ -1958,15 +1913,11 @@ async fn same_context_background_session_can_reset_its_own_network_conditions_be
|
||||
);
|
||||
let staged = active
|
||||
.background_target(&second_target_id)
|
||||
.filter(|target| target.has_non_default_session_state())
|
||||
.expect("second target should have staged background page session state");
|
||||
assert!(!staged.network_policy.network_offline());
|
||||
assert_eq!(staged.network_policy.emulated_network_latency(), 0.0);
|
||||
assert_eq!(staged.network_policy.emulated_download_throughput(), -1.0);
|
||||
assert_eq!(staged.network_policy.emulated_upload_throughput(), -1.0);
|
||||
assert_eq!(
|
||||
staged.network_policy.emulated_connection_type(),
|
||||
Some("none")
|
||||
assert!(
|
||||
!staged.has_non_default_session_state(),
|
||||
"offline reset should return to the default policy"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2011,34 +1962,6 @@ async fn same_context_background_session_can_reset_its_own_network_conditions_be
|
||||
.network_policy
|
||||
.network_offline()
|
||||
);
|
||||
assert_eq!(
|
||||
activated
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_network_latency(),
|
||||
0.0
|
||||
);
|
||||
assert_eq!(
|
||||
activated
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_download_throughput(),
|
||||
-1.0
|
||||
);
|
||||
assert_eq!(
|
||||
activated
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_upload_throughput(),
|
||||
-1.0
|
||||
);
|
||||
assert_eq!(
|
||||
activated
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_connection_type(),
|
||||
Some("none")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -275,10 +275,9 @@ async fn same_context_targets_restore_their_own_network_conditions_after_session
|
||||
"sessionId": "SID-active",
|
||||
"params": {
|
||||
"offline": false,
|
||||
"latency": 150,
|
||||
"downloadThroughput": 1024,
|
||||
"uploadThroughput": 512,
|
||||
"connectionType": "cellular3g"
|
||||
"latency": 0,
|
||||
"downloadThroughput": -1,
|
||||
"uploadThroughput": -1,
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
@@ -333,10 +332,9 @@ async fn same_context_targets_restore_their_own_network_conditions_after_session
|
||||
"sessionId": second_session_id,
|
||||
"params": {
|
||||
"offline": true,
|
||||
"latency": 25,
|
||||
"downloadThroughput": 2048,
|
||||
"uploadThroughput": 256,
|
||||
"connectionType": "wifi"
|
||||
"latency": 0,
|
||||
"downloadThroughput": -1,
|
||||
"uploadThroughput": -1,
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
@@ -376,19 +374,6 @@ async fn same_context_targets_restore_their_own_network_conditions_after_session
|
||||
.filter(|target| target.has_non_default_session_state())
|
||||
.expect("second target should keep background network state");
|
||||
assert!(background.network_policy.network_offline());
|
||||
assert_eq!(background.network_policy.emulated_network_latency(), 25.0);
|
||||
assert_eq!(
|
||||
background.network_policy.emulated_download_throughput(),
|
||||
2048.0
|
||||
);
|
||||
assert_eq!(
|
||||
background.network_policy.emulated_upload_throughput(),
|
||||
256.0
|
||||
);
|
||||
assert_eq!(
|
||||
background.network_policy.emulated_connection_type(),
|
||||
Some("wifi")
|
||||
);
|
||||
}
|
||||
|
||||
ctx.process_async(json!({
|
||||
@@ -409,34 +394,6 @@ async fn same_context_targets_restore_their_own_network_conditions_after_session
|
||||
.expect("active browser context after restoring first target");
|
||||
assert_eq!(active.active_target_id(), Some("TID-000000000NA"));
|
||||
assert!(!active.active_page_target().network_policy.network_offline());
|
||||
assert_eq!(
|
||||
active
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_network_latency(),
|
||||
150.0
|
||||
);
|
||||
assert_eq!(
|
||||
active
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_download_throughput(),
|
||||
1024.0
|
||||
);
|
||||
assert_eq!(
|
||||
active
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_upload_throughput(),
|
||||
512.0
|
||||
);
|
||||
assert_eq!(
|
||||
active
|
||||
.active_page_target()
|
||||
.network_policy
|
||||
.emulated_connection_type(),
|
||||
Some("cellular3g")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -629,18 +629,6 @@ impl RendererInspectorCommandEnvelope {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn uses_cpu_throttling(&self) -> bool {
|
||||
matches!(
|
||||
self.main_thread_payload(),
|
||||
RendererInspectorPageCommand::DispatchRuntimeProtocolMessage { .. }
|
||||
| RendererInspectorPageCommand::DispatchRuntimeProtocolMessageWithDeferredResponse { .. }
|
||||
| RendererInspectorPageCommand::DispatchRuntimeProtocolMessageWithContextResolution { .. }
|
||||
| RendererInspectorPageCommand::DispatchRuntimeProtocolMessageWithContextResolutionAndDeferredResponse { .. }
|
||||
| RendererInspectorPageCommand::DomDebuggerGetEventListeners { .. }
|
||||
| RendererInspectorPageCommand::FocusDocumentNodeForObjectId { .. }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn main_protocol_can_dispatch_at_inspector_session_boundary(
|
||||
|
||||
@@ -2,18 +2,6 @@ use super::page_surface::RendererInspectorPageCommand;
|
||||
use super::*;
|
||||
|
||||
impl PageVm {
|
||||
pub(in crate::runtime) async fn dispatch_renderer_page_command_async(
|
||||
&mut self,
|
||||
command: RendererPageCommand,
|
||||
) -> Result<RendererPageReply> {
|
||||
let throttling_started =
|
||||
renderer_page_command_uses_cpu_throttling(&command).then(std::time::Instant::now);
|
||||
let result = self.dispatch_renderer_page_command(command);
|
||||
self.apply_cpu_throttling_delay_after_page_command(throttling_started)
|
||||
.await;
|
||||
result
|
||||
}
|
||||
|
||||
pub(in crate::runtime) fn dispatch_renderer_page_command(
|
||||
&mut self,
|
||||
command: RendererPageCommand,
|
||||
@@ -1125,7 +1113,10 @@ impl PageVm {
|
||||
Ok(RendererPageReply::Unit)
|
||||
}
|
||||
RendererPageCommand::SetCpuThrottlingRate(rate) => {
|
||||
self.set_cpu_throttling_rate(rate);
|
||||
if !rate.is_finite() || rate > 1.0 {
|
||||
return Err(anyhow!("CPU throttling is not supported"));
|
||||
}
|
||||
self.set_cpu_throttling_rate(1.0);
|
||||
Ok(RendererPageReply::Unit)
|
||||
}
|
||||
RendererPageCommand::SetEmulatedMedia(overrides) => {
|
||||
@@ -1385,31 +1376,6 @@ impl PageVm {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn apply_cpu_throttling_delay_after_page_command(
|
||||
&self,
|
||||
started: Option<std::time::Instant>,
|
||||
) {
|
||||
let Some(started) = started else {
|
||||
return;
|
||||
};
|
||||
let rate = self.cpu_throttling_rate;
|
||||
if !rate.is_finite() || rate <= 1.0 {
|
||||
return;
|
||||
}
|
||||
let elapsed = started.elapsed();
|
||||
if elapsed.is_zero() {
|
||||
return;
|
||||
}
|
||||
let delay_secs = elapsed.as_secs_f64() * (rate - 1.0);
|
||||
if !delay_secs.is_finite() || delay_secs <= 0.0 {
|
||||
return;
|
||||
}
|
||||
tokio::time::sleep(std::time::Duration::from_secs_f64(
|
||||
delay_secs.min(std::time::Duration::MAX.as_secs_f64()),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
fn renderer_page_command_action_barrier(
|
||||
@@ -1431,33 +1397,3 @@ fn renderer_page_command_action_barrier(
|
||||
_ => Some(moli_action_window::ActionBarrier::Explicit),
|
||||
}
|
||||
}
|
||||
|
||||
fn renderer_page_command_uses_cpu_throttling(command: &RendererPageCommand) -> bool {
|
||||
if let RendererPageCommand::Inspector(envelope) = command {
|
||||
return envelope.uses_cpu_throttling();
|
||||
}
|
||||
matches!(
|
||||
command,
|
||||
RendererPageCommand::EvaluateExpression { .. }
|
||||
| RendererPageCommand::EvaluateExpressionByValue { .. }
|
||||
| RendererPageCommand::EvaluateExpressionAndFollowPendingNavigation { .. }
|
||||
| RendererPageCommand::EvaluateExpressionInExecutionContext { .. }
|
||||
| RendererPageCommand::EvaluateExpressionInExecutionContextAndFollowPendingNavigation { .. }
|
||||
| RendererPageCommand::DispatchMouseEventAtPoint { .. }
|
||||
| RendererPageCommand::DispatchTouchEvent { .. }
|
||||
| RendererPageCommand::DispatchDragEventAtPoint { .. }
|
||||
| RendererPageCommand::InsertTextIntoActiveControl(_)
|
||||
| RendererPageCommand::DispatchKeyEvent { .. }
|
||||
| RendererPageCommand::DomDebuggerConfigureEventListenerBreakpoint { .. }
|
||||
| RendererPageCommand::DomDebuggerConfigureXhrBreakpoint { .. }
|
||||
| RendererPageCommand::DomDebuggerConfigureDomBreakpoint { .. }
|
||||
| RendererPageCommand::PerformanceMetricSnapshot
|
||||
| RendererPageCommand::CreateIsolatedWorldRuntimeActivity { .. }
|
||||
| RendererPageCommand::AddDocumentStartScriptRuntimeActivity { .. }
|
||||
| RendererPageCommand::RunPageSurfaceOverrideScript { .. }
|
||||
| RendererPageCommand::MutateDocumentBackendNodeAttribute { .. }
|
||||
| RendererPageCommand::EditDocumentNode { .. }
|
||||
| RendererPageCommand::FocusDocumentBackendNode { .. }
|
||||
| RendererPageCommand::SetDocumentContent { .. }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ impl RendererPageSlotHandle {
|
||||
page_id.as_u64()
|
||||
);
|
||||
self.begin_command(page_id, command_epoch)?;
|
||||
let result = vm.dispatch_renderer_page_command_async(command).await;
|
||||
let result = vm.dispatch_renderer_page_command(command);
|
||||
self.finish_command(page_id, command_epoch)?;
|
||||
let reply = result?;
|
||||
self.update_command_epoch(page_id, command_epoch)?;
|
||||
|
||||
@@ -173,12 +173,11 @@ new MutationObserver(records => {
|
||||
.expect("test Page should expose its root frame")
|
||||
.to_owned();
|
||||
|
||||
let reply = page_vm
|
||||
.dispatch_renderer_page_command_async(RendererPageCommand::SetDocumentContent {
|
||||
let reply =
|
||||
page_vm.dispatch_renderer_page_command(RendererPageCommand::SetDocumentContent {
|
||||
frame_id,
|
||||
html: "<main>after</main>".to_owned(),
|
||||
})
|
||||
.await?;
|
||||
})?;
|
||||
assert!(matches!(
|
||||
reply,
|
||||
RendererPageReply::SetDocumentContentResult(RendererSetDocumentContentResult::Updated)
|
||||
@@ -233,14 +232,12 @@ Promise.resolve().then(() => __fetchCommandCheckpoint += 1);
|
||||
)?;
|
||||
page_vm.vm_mut().enqueue_test_pending_runtime_source_load();
|
||||
|
||||
let reply = page_vm
|
||||
.dispatch_renderer_page_command_async(
|
||||
RendererPageCommand::FailPendingSubresourceFetch {
|
||||
internal_id: pending[0].internal_id,
|
||||
error_text: "command checkpoint witness".to_owned(),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let reply = page_vm.dispatch_renderer_page_command(
|
||||
RendererPageCommand::FailPendingSubresourceFetch {
|
||||
internal_id: pending[0].internal_id,
|
||||
error_text: "command checkpoint witness".to_owned(),
|
||||
},
|
||||
)?;
|
||||
assert!(matches!(reply, RendererPageReply::Unit));
|
||||
assert_eq!(
|
||||
page_vm
|
||||
@@ -291,9 +288,8 @@ fetch("https://offline-command-checkpoint.test/resource").then(
|
||||
"the fixture should pause one Window Fetch"
|
||||
);
|
||||
|
||||
let reply = page_vm
|
||||
.dispatch_renderer_page_command_async(RendererPageCommand::SetNetworkOffline(true))
|
||||
.await?;
|
||||
let reply =
|
||||
page_vm.dispatch_renderer_page_command(RendererPageCommand::SetNetworkOffline(true))?;
|
||||
assert!(matches!(reply, RendererPageReply::Unit));
|
||||
page_vm
|
||||
.vm_mut()
|
||||
@@ -306,19 +302,17 @@ Promise.resolve().then(() => __offlineContinueCheckpoint += 1);
|
||||
)?;
|
||||
page_vm.vm_mut().enqueue_test_pending_runtime_source_load();
|
||||
|
||||
let reply = page_vm
|
||||
.dispatch_renderer_page_command_async(
|
||||
RendererPageCommand::ContinuePendingSubresourceFetch {
|
||||
internal_id: pending[0].internal_id,
|
||||
url: None,
|
||||
method: None,
|
||||
body: None,
|
||||
headers: None,
|
||||
intercept_response: false,
|
||||
handle_auth_requests: false,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let reply = page_vm.dispatch_renderer_page_command(
|
||||
RendererPageCommand::ContinuePendingSubresourceFetch {
|
||||
internal_id: pending[0].internal_id,
|
||||
url: None,
|
||||
method: None,
|
||||
body: None,
|
||||
headers: None,
|
||||
intercept_response: false,
|
||||
handle_auth_requests: false,
|
||||
},
|
||||
)?;
|
||||
assert!(matches!(
|
||||
reply,
|
||||
RendererPageReply::PendingSubresourceContinueOutcome(
|
||||
@@ -377,15 +371,12 @@ Promise.resolve().then(() => __rejectedFetchCommandCheckpoint += 1);
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let error = match page_vm
|
||||
.dispatch_renderer_page_command_async(
|
||||
RendererPageCommand::FailPendingSubresourceFetch {
|
||||
internal_id: u64::MAX,
|
||||
error_text: "missing request".to_owned(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
let error = match page_vm.dispatch_renderer_page_command(
|
||||
RendererPageCommand::FailPendingSubresourceFetch {
|
||||
internal_id: u64::MAX,
|
||||
error_text: "missing request".to_owned(),
|
||||
},
|
||||
) {
|
||||
Ok(_) => panic!("an unknown interception request should be rejected"),
|
||||
Err(error) => error,
|
||||
};
|
||||
|
||||
@@ -1461,10 +1461,7 @@ window.addEventListener("pageshow", () => {
|
||||
.eval("__stopLoadingLifecycleEvents.length = 0; 'cleared'")?;
|
||||
|
||||
let reply = page_vm
|
||||
.dispatch_renderer_page_command_async(
|
||||
RendererPageCommand::StopDocumentLifecycle,
|
||||
)
|
||||
.await?;
|
||||
.dispatch_renderer_page_command(RendererPageCommand::StopDocumentLifecycle)?;
|
||||
assert!(matches!(reply, RendererPageReply::Unit));
|
||||
assert_eq!(
|
||||
page_vm.vm_mut().eval(
|
||||
|
||||
@@ -14192,8 +14192,7 @@ async fn direct_page_vm_wait_commands_fail_closed() {
|
||||
for (command, expected_error) in commands {
|
||||
let mut page_vm = test_page_vm();
|
||||
let error = match page_vm
|
||||
.dispatch_renderer_page_command_async(command)
|
||||
.await
|
||||
.dispatch_renderer_page_command(command)
|
||||
{
|
||||
Ok(_) => panic!("direct wait command should fail closed"),
|
||||
Err(error) => error,
|
||||
|
||||
@@ -2177,43 +2177,29 @@ document.body.appendChild(frame);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn cpu_throttling_rate_slows_runtime_evaluate_command() {
|
||||
async fn cpu_throttling_rate_rejects_unavailable_rates() {
|
||||
let runtime = JsRuntime::initialize();
|
||||
let loader =
|
||||
ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).expect("default loader");
|
||||
let loader = ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).unwrap();
|
||||
let url = url::Url::parse("https://example.test/cpu-throttling").unwrap();
|
||||
let mut page = create_test_html_page(&runtime, &loader, url, "<!doctype html>").await;
|
||||
|
||||
let (reply, _) = page
|
||||
let error = page
|
||||
.run_async_command(RendererPageCommand::SetCpuThrottlingRate(3.0))
|
||||
.await
|
||||
.expect("CPU throttling rate should update live page");
|
||||
assert!(matches!(reply, RendererPageReply::Unit));
|
||||
|
||||
let started = std::time::Instant::now();
|
||||
let (reply, _) = page
|
||||
.run_async_command(RendererPageCommand::EvaluateExpression {
|
||||
expression: r#"
|
||||
(() => {
|
||||
const end = Date.now() + 40;
|
||||
while (Date.now() < end) {}
|
||||
return true;
|
||||
})()
|
||||
"#
|
||||
.to_owned(),
|
||||
await_promise: false,
|
||||
})
|
||||
.await
|
||||
.expect("throttled evaluate should run");
|
||||
assert_eq!(renderer_json_value(reply), Some(serde_json::json!(true)));
|
||||
.err()
|
||||
.expect("unavailable throttling must fail");
|
||||
assert!(
|
||||
started.elapsed() >= Duration::from_millis(75),
|
||||
"rate=3 should add renderer-side delay to a CPU-bound evaluate command"
|
||||
error
|
||||
.to_string()
|
||||
.contains("CPU throttling is not supported")
|
||||
);
|
||||
|
||||
page.close_async()
|
||||
.await
|
||||
.expect("CPU throttling test page should close");
|
||||
for rate in [0.0, 1.0] {
|
||||
let (reply, _) = page
|
||||
.run_async_command(RendererPageCommand::SetCpuThrottlingRate(rate))
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(matches!(reply, RendererPageReply::Unit));
|
||||
}
|
||||
page.close_async().await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
|
||||
Reference in New Issue
Block a user