mirror of
https://github.com/lexmount/moli.git
synced 2026-09-28 08:01:37 +00:00
Split subresource fetch handling by lifecycle
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,333 @@
|
||||
use super::*;
|
||||
|
||||
impl ScriptVm {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn current_document_write_external_script_fetch_target(
|
||||
&self,
|
||||
) -> Option<crate::types::DocumentWriteExternalScriptFetchTarget> {
|
||||
let target = self
|
||||
.document_runtime
|
||||
.pending_document_write_external_script_fetch_target()?;
|
||||
self.document_write_external_script_fetch_target_is_current(target)
|
||||
.then_some(target)
|
||||
}
|
||||
pub(crate) fn document_write_external_script_fetch_target_is_current(
|
||||
&self,
|
||||
expected: crate::types::DocumentWriteExternalScriptFetchTarget,
|
||||
) -> bool {
|
||||
self.document_runtime
|
||||
.has_document_write_external_script_fetch_target(expected)
|
||||
&& self.current_main_document_task_owner() == Some(expected.task_owner())
|
||||
}
|
||||
pub(crate) fn apply_current_document_write_external_script_load_completion(
|
||||
&mut self,
|
||||
completion: crate::runtime::AuthorizedCurrentDocumentWriteExternalScriptLoadCompletion,
|
||||
) -> Result<crate::document_runtime::DocumentWriteExternalScriptLoadApplication> {
|
||||
let completion = completion.into_completion();
|
||||
let document_runtime: *mut DocumentRuntime = &mut *self.document_runtime;
|
||||
self.with_default_context_scope(|scope, host_ptr| {
|
||||
Ok(unsafe { &mut *document_runtime }
|
||||
.complete_document_write_external_script_load(scope, host_ptr, completion))
|
||||
})
|
||||
}
|
||||
pub(crate) fn apply_current_child_document_load_completion(
|
||||
&mut self,
|
||||
authorization: AuthorizedCurrentChildDocumentLoadCompletion,
|
||||
) -> Result<CurrentChildDocumentLoadApplication> {
|
||||
self.apply_current_child_document_load_completion_inner(authorization.into_completion())
|
||||
}
|
||||
pub(super) fn apply_current_child_document_load_completion_inner(
|
||||
&mut self,
|
||||
completion: ChildDocumentLoadCompletion,
|
||||
) -> Result<CurrentChildDocumentLoadApplication> {
|
||||
let context_host = self._context_host.clone();
|
||||
let application = self.with_default_context_scope(move |scope, _host_ptr| {
|
||||
Ok(context_host
|
||||
.borrow_mut()
|
||||
.apply_current_child_document_load_completion(scope, completion))
|
||||
})?;
|
||||
let application = match application {
|
||||
crate::native_bridge::ChildDocumentLoadApplication::Applied {
|
||||
followup,
|
||||
body_activity,
|
||||
} => (followup.map(|application| *application), body_activity),
|
||||
crate::native_bridge::ChildDocumentLoadApplication::SupersededDuringApplication {
|
||||
completion,
|
||||
body_activity,
|
||||
} => {
|
||||
let historical_network_recorded =
|
||||
self.record_historical_child_document_load_network(&completion);
|
||||
self.apply_pending_child_document_owner_retirements();
|
||||
return Ok(
|
||||
CurrentChildDocumentLoadApplication::SupersededDuringApplication {
|
||||
historical_network_recorded,
|
||||
body_activity,
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
self.apply_pending_child_document_owner_retirements();
|
||||
let (application, body_activity) = application;
|
||||
let Some(application) = application else {
|
||||
return Ok(CurrentChildDocumentLoadApplication::Applied { body_activity });
|
||||
};
|
||||
let (work, parser_stop_action, owner_transition) = application.into_followups();
|
||||
if let Some(transition) = owner_transition {
|
||||
self.apply_child_document_owner_transition(transition);
|
||||
}
|
||||
if let Some(action) = parser_stop_action {
|
||||
super::super::child_document_lifecycle::ChildDocumentLifecycleOwner::new(self)
|
||||
.notify_parser_stop_action(action);
|
||||
}
|
||||
if let Some(work) = work {
|
||||
super::super::child_document_script_scheduler::ChildDocumentScriptSchedulerOwner::new(
|
||||
self,
|
||||
)
|
||||
.notify_parser_classic_next_owner_action(work);
|
||||
}
|
||||
Ok(CurrentChildDocumentLoadApplication::Applied { body_activity })
|
||||
}
|
||||
pub(crate) fn current_child_document_navigation_fetch_target(
|
||||
&self,
|
||||
child_handle: crate::document_runtime::DomHandle,
|
||||
) -> Option<crate::frame_owner_model::ChildDocumentNavigationFetchTarget> {
|
||||
self._context_host
|
||||
.borrow()
|
||||
.current_child_document_navigation_fetch_target(child_handle)
|
||||
}
|
||||
pub(crate) fn record_historical_child_document_load_network(
|
||||
&mut self,
|
||||
completion: &ChildDocumentLoadCompletion,
|
||||
) -> bool {
|
||||
self._context_host
|
||||
.borrow_mut()
|
||||
.record_historical_child_document_load_network(completion)
|
||||
}
|
||||
pub(crate) fn discard_stale_child_document_load_completion(
|
||||
&mut self,
|
||||
target: crate::frame_owner_model::ChildDocumentNavigationFetchTarget,
|
||||
) {
|
||||
self._context_host
|
||||
.borrow_mut()
|
||||
.discard_stale_child_document_load_completion(target);
|
||||
}
|
||||
pub(crate) fn apply_child_blocking_stylesheet_load_completion_from_page_turn(
|
||||
&mut self,
|
||||
completion: ChildBlockingStylesheetLoadCompletion,
|
||||
) -> Result<()> {
|
||||
let context_host = self._context_host.clone();
|
||||
self.with_default_context_scope(move |scope, _host_ptr| {
|
||||
context_host
|
||||
.borrow_mut()
|
||||
.apply_child_blocking_stylesheet_load_completion(scope, completion);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
pub(crate) fn record_historical_child_blocking_stylesheet_network_results(
|
||||
&mut self,
|
||||
completion: &ChildBlockingStylesheetLoadCompletion,
|
||||
) {
|
||||
self._context_host
|
||||
.borrow_mut()
|
||||
.record_historical_child_blocking_stylesheet_network_results(completion);
|
||||
}
|
||||
pub(crate) fn current_child_document_task_owner(
|
||||
&self,
|
||||
child_handle: crate::document_runtime::DomHandle,
|
||||
) -> Option<crate::frame_owner_model::FrameDocumentTaskOwner> {
|
||||
self._context_host
|
||||
.borrow()
|
||||
.current_child_document_task_owner(child_handle)
|
||||
}
|
||||
pub(crate) fn current_child_document_module_fetch_target(
|
||||
&self,
|
||||
child_handle: crate::document_runtime::DomHandle,
|
||||
) -> Option<crate::frame_owner_model::ChildDocumentModuleFetchTarget> {
|
||||
self._context_host
|
||||
.borrow()
|
||||
.current_child_document_module_fetch_target(child_handle)
|
||||
}
|
||||
pub(crate) fn apply_child_classic_script_load_completion_from_page_turn(
|
||||
&mut self,
|
||||
completion: ChildClassicScriptLoadCompletion,
|
||||
) -> Result<()> {
|
||||
let context_host = self._context_host.clone();
|
||||
let application = self.with_default_context_scope(move |_scope, _host_ptr| {
|
||||
Ok(context_host
|
||||
.borrow_mut()
|
||||
.apply_child_classic_script_load_completion(completion))
|
||||
})?;
|
||||
if let Some(application) = application {
|
||||
if let Some(work) = application.scheduler_work {
|
||||
super::super::child_document_script_scheduler::ChildDocumentScriptSchedulerOwner::new(
|
||||
self,
|
||||
)
|
||||
.notify_parser_classic_next_owner_action(work);
|
||||
return Ok(());
|
||||
}
|
||||
let _ = application.queued_document_script_ready;
|
||||
let _ = application.queued_document_lifecycle;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub(crate) fn record_historical_child_classic_script_network_result(
|
||||
&mut self,
|
||||
completion: &ChildClassicScriptLoadCompletion,
|
||||
) -> bool {
|
||||
self._context_host
|
||||
.borrow_mut()
|
||||
.record_historical_child_classic_script_network_result(completion)
|
||||
}
|
||||
/// Applies a parser-module terminal only after the Page owner has proved
|
||||
/// that its complete exact target is current.
|
||||
pub(crate) fn apply_current_child_parser_module_root_fetch_completion(
|
||||
&mut self,
|
||||
authorization: AuthorizedCurrentChildModuleFetchCompletion<
|
||||
ChildParserModuleRootFetchCompletion,
|
||||
>,
|
||||
) -> FrameDocumentModuleTerminalQueueFollowup {
|
||||
self.finish_child_parser_module_root_fetch_completion(authorization.into_completion())
|
||||
}
|
||||
pub(super) fn finish_child_parser_module_root_fetch_completion(
|
||||
&mut self,
|
||||
completion: ChildParserModuleRootFetchCompletion,
|
||||
) -> FrameDocumentModuleTerminalQueueFollowup {
|
||||
let applied = self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.finish_child_parser_module_root_fetch_request(&completion);
|
||||
if applied {
|
||||
return self.apply_child_parser_module_root_fetch_completion_to_owner(completion);
|
||||
}
|
||||
FrameDocumentModuleTerminalQueueFollowup::none()
|
||||
}
|
||||
/// Applies a dependency terminal only after the Page owner has proved
|
||||
/// that its complete exact target is current.
|
||||
pub(crate) fn apply_current_child_module_dependency_fetch_completion(
|
||||
&mut self,
|
||||
authorization: AuthorizedCurrentChildModuleFetchCompletion<
|
||||
ChildModuleDependencyFetchCompletion,
|
||||
>,
|
||||
) -> FrameDocumentModuleTerminalQueueFollowup {
|
||||
self.finish_child_module_dependency_fetch_completion(authorization.into_completion())
|
||||
}
|
||||
/// Applies a child `modulepreload` terminal only after the Page owner has
|
||||
/// proved its complete exact target is current.
|
||||
pub(crate) fn apply_current_child_modulepreload_fetch_completion(
|
||||
&mut self,
|
||||
authorization: AuthorizedCurrentChildModuleFetchCompletion<
|
||||
ChildModulepreloadFetchCompletion,
|
||||
>,
|
||||
) -> FrameDocumentModuleTerminalQueueFollowup {
|
||||
super::super::child_module_fetch::ChildModuleFetchOwner::new(self)
|
||||
.apply_current_modulepreload_fetch_completion(authorization.into_completion())
|
||||
}
|
||||
pub(super) fn finish_child_module_dependency_fetch_completion(
|
||||
&mut self,
|
||||
completion: ChildModuleDependencyFetchCompletion,
|
||||
) -> FrameDocumentModuleTerminalQueueFollowup {
|
||||
let applied = self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.finish_child_module_dependency_fetch_request(&completion);
|
||||
if applied {
|
||||
return self.apply_child_module_dependency_fetch_completion_to_owner(completion);
|
||||
}
|
||||
FrameDocumentModuleTerminalQueueFollowup::none()
|
||||
}
|
||||
pub(crate) fn record_current_child_module_fetch_network_result(
|
||||
&mut self,
|
||||
attribution: &crate::types::ChildModuleFetchNetworkAttribution,
|
||||
network_result: Option<&crate::types::SharedNavigationResponseResult>,
|
||||
) -> bool {
|
||||
let Some(network_result) = network_result else {
|
||||
return false;
|
||||
};
|
||||
self._context_host
|
||||
.borrow_mut()
|
||||
.record_current_child_module_fetch_network_result(attribution, network_result.as_ref());
|
||||
true
|
||||
}
|
||||
pub(crate) fn record_historical_child_module_fetch_network_result(
|
||||
&mut self,
|
||||
attribution: &crate::types::ChildModuleFetchNetworkAttribution,
|
||||
network_result: Option<&crate::types::SharedNavigationResponseResult>,
|
||||
) -> bool {
|
||||
let Some(network_result) = network_result else {
|
||||
return false;
|
||||
};
|
||||
self._context_host
|
||||
.borrow_mut()
|
||||
.record_historical_child_module_fetch_network_result(
|
||||
attribution,
|
||||
network_result.as_ref(),
|
||||
);
|
||||
true
|
||||
}
|
||||
#[cfg(test)]
|
||||
pub(crate) fn complete_popup_document_load(
|
||||
&mut self,
|
||||
completion: PopupDocumentLoadCompletion,
|
||||
) -> Result<()> {
|
||||
let target = completion.target();
|
||||
if self.current_lightweight_popup_document_fetch_target(target.load_id()) != Some(target) {
|
||||
return Ok(());
|
||||
}
|
||||
let _ = self.apply_popup_document_load_completion_inner(completion)?;
|
||||
Ok(())
|
||||
}
|
||||
pub(crate) fn current_lightweight_popup_document_fetch_target(
|
||||
&self,
|
||||
load_id: u64,
|
||||
) -> Option<crate::native_bridge::LightweightPopupDocumentFetchTarget> {
|
||||
self._context_host
|
||||
.borrow()
|
||||
.current_lightweight_popup_document_fetch_target(load_id)
|
||||
}
|
||||
pub(crate) fn apply_current_popup_document_load_completion(
|
||||
&mut self,
|
||||
authorization: AuthorizedCurrentPopupDocumentLoadCompletion,
|
||||
) -> Result<crate::native_bridge::PopupDocumentLoadApplication> {
|
||||
self.apply_popup_document_load_completion_inner(authorization.into_completion())
|
||||
}
|
||||
pub(super) fn apply_popup_document_load_completion_inner(
|
||||
&mut self,
|
||||
completion: PopupDocumentLoadCompletion,
|
||||
) -> Result<crate::native_bridge::PopupDocumentLoadApplication> {
|
||||
let context_host = self._context_host.clone();
|
||||
self.with_default_context_scope(move |scope, _host_ptr| {
|
||||
let application = context_host
|
||||
.borrow_mut()
|
||||
.apply_lightweight_popup_document_load_completion(scope, completion);
|
||||
Ok(application)
|
||||
})
|
||||
}
|
||||
pub(crate) fn current_lightweight_popup_classic_script_fetch_target(
|
||||
&self,
|
||||
load_id: u64,
|
||||
) -> Option<crate::native_bridge::LightweightPopupClassicScriptFetchTarget> {
|
||||
self._context_host
|
||||
.borrow()
|
||||
.current_lightweight_popup_classic_script_fetch_target(load_id)
|
||||
}
|
||||
pub(crate) fn discard_stale_lightweight_popup_classic_script_completion(
|
||||
&mut self,
|
||||
target: crate::native_bridge::LightweightPopupClassicScriptFetchTarget,
|
||||
) {
|
||||
self._context_host
|
||||
.borrow_mut()
|
||||
.discard_stale_lightweight_popup_classic_script_completion(target);
|
||||
}
|
||||
pub(crate) fn apply_current_popup_classic_script_load_completion(
|
||||
&mut self,
|
||||
authorization: AuthorizedCurrentPopupClassicScriptLoadCompletion,
|
||||
) -> Result<crate::native_bridge::PopupClassicScriptLoadApplication> {
|
||||
let completion: PopupClassicScriptLoadCompletion = authorization.into_completion();
|
||||
let context_host = self._context_host.clone();
|
||||
self.with_default_context_scope(move |scope, _host_ptr| {
|
||||
Ok(context_host
|
||||
.borrow_mut()
|
||||
.apply_lightweight_popup_classic_script_load_completion(scope, completion))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,955 @@
|
||||
use super::*;
|
||||
|
||||
impl ScriptVm {
|
||||
pub(super) fn resolve_network_only_subresource_fetch(
|
||||
&mut self,
|
||||
mut pending: PendingSubresourceFetchState,
|
||||
request_url: Url,
|
||||
request_method: String,
|
||||
request_headers: moli_fetch::RequestHeaders,
|
||||
request_body: Option<String>,
|
||||
response_status_text: Option<String>,
|
||||
skip_fetch_security_validation: bool,
|
||||
network_error_text: Option<String>,
|
||||
result: std::result::Result<crate::protocol_types::NavigationResponse, String>,
|
||||
) -> Result<()> {
|
||||
let detached_window_fetch = pending.continuation.is_detached_window_fetch();
|
||||
debug_assert!(
|
||||
detached_window_fetch || pending.execution_context.is_window_network_only(),
|
||||
"network-only completion must be an accepted fire-and-forget request or detached Fetch"
|
||||
);
|
||||
self._context_host
|
||||
.borrow_mut()
|
||||
.record_deferred_pending_subresource_request_started(&mut pending);
|
||||
|
||||
let result = if detached_window_fetch {
|
||||
result.and_then(|response| {
|
||||
if !response.redirect_chain.is_empty()
|
||||
&& let Some(message) = detached_window_fetch_csp_redirect_failure_message(
|
||||
&self._context_host,
|
||||
&pending,
|
||||
&response.final_url,
|
||||
)
|
||||
{
|
||||
return Err(message);
|
||||
}
|
||||
if !skip_fetch_security_validation {
|
||||
crate::network_host::validate_fetch_response_security_policy_with_body(
|
||||
&pending.request_origin,
|
||||
&response.head(),
|
||||
response.body_bytes(),
|
||||
pending.request_mode,
|
||||
pending.credentials_mode,
|
||||
pending.policy_context,
|
||||
)?;
|
||||
}
|
||||
Ok(response)
|
||||
})
|
||||
} else {
|
||||
result
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(response) => {
|
||||
let network_request_headers = response
|
||||
.network_request_headers()
|
||||
.map(|headers| headers.to_vec());
|
||||
let request_cookie_report = response
|
||||
.request_cookie_report
|
||||
.clone()
|
||||
.or_else(|| pending.info.request_cookie_report.clone());
|
||||
let response_body = SubresourceResponseBody::from_navigation_response(&response);
|
||||
let mut network_record = crate::types::SubresourceNetworkRecord::success_with_body(
|
||||
pending.info.frame_id.clone(),
|
||||
pending.info.document_url.clone(),
|
||||
request_url,
|
||||
request_method,
|
||||
request_headers,
|
||||
request_body,
|
||||
pending.info.resource_type,
|
||||
request_cookie_report,
|
||||
response.redirect_chain,
|
||||
response.final_url,
|
||||
response.status,
|
||||
response.headers,
|
||||
response_body,
|
||||
response.cookie_set_reports,
|
||||
)
|
||||
.with_from_cache(response.from_cache)
|
||||
.with_negotiated_http_version(response.negotiated_http_version)
|
||||
.with_network_request_headers(network_request_headers)
|
||||
.with_request_initiator_type(SubresourceRequestInitiatorType::Script)
|
||||
.with_request_body_bytes(pending.info.request_body_bytes.clone());
|
||||
if let Some(status_text) = response_status_text.as_deref() {
|
||||
network_record = network_record.with_response_status_text(status_text);
|
||||
}
|
||||
if let Some(handle) = pending.info.network_request_handle {
|
||||
network_record = network_record.with_request_handle(handle);
|
||||
}
|
||||
self._context_host
|
||||
.borrow_mut()
|
||||
.record_subresource_network(network_record);
|
||||
}
|
||||
Err(error_text) => {
|
||||
let error_text = network_error_text.as_deref().unwrap_or(&error_text);
|
||||
let mut network_record = crate::types::SubresourceNetworkRecord::failure(
|
||||
pending.info.frame_id.clone(),
|
||||
pending.info.document_url.clone(),
|
||||
request_url,
|
||||
request_method,
|
||||
request_headers,
|
||||
request_body,
|
||||
pending.info.resource_type,
|
||||
error_text.to_owned(),
|
||||
)
|
||||
.with_request_initiator_type(SubresourceRequestInitiatorType::Script)
|
||||
.with_request_body_bytes(pending.info.request_body_bytes.clone());
|
||||
if let Some(handle) = pending.info.network_request_handle {
|
||||
network_record = network_record.with_request_handle(handle);
|
||||
}
|
||||
self._context_host
|
||||
.borrow_mut()
|
||||
.record_subresource_network(network_record);
|
||||
}
|
||||
}
|
||||
tracing::debug!(
|
||||
internal_id = pending.info.internal_id,
|
||||
detached_owner = ?pending.execution_context.detached_window_fetch_identity(),
|
||||
accepted_context = ?pending.execution_context.window_network_only_identity(),
|
||||
accepted_document = ?pending.execution_context.window_document_network_only_identity(),
|
||||
"completed network-only subresource without entering V8"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn resolve_pending_event_source_fetch(
|
||||
&mut self,
|
||||
mut pending: PendingSubresourceFetchState,
|
||||
request_url: Url,
|
||||
request_method: String,
|
||||
request_headers: moli_fetch::RequestHeaders,
|
||||
request_body: Option<String>,
|
||||
response_status_text: Option<String>,
|
||||
skip_fetch_security_validation: bool,
|
||||
network_error_text: Option<String>,
|
||||
result: std::result::Result<crate::protocol_types::NavigationResponse, String>,
|
||||
) -> Result<AsyncSubresourceFetchBodyActivity> {
|
||||
self._context_host
|
||||
.borrow_mut()
|
||||
.record_deferred_pending_subresource_request_started(&mut pending);
|
||||
let context_host = self._context_host.clone();
|
||||
self.renderer_document_isolate
|
||||
.with_entered_renderer_document_isolate(|isolate| {
|
||||
let scope = pin!(v8::HandleScope::new(isolate));
|
||||
let scope = &mut scope.init();
|
||||
let context = v8::Local::new(
|
||||
scope,
|
||||
pending
|
||||
.execution_context
|
||||
.context_global()
|
||||
.expect("EventSource completion must retain its V8 context"),
|
||||
);
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
if !window_subresource_realm_is_current(&context_host, scope, &pending) {
|
||||
return Ok(AsyncSubresourceFetchBodyActivity::NoWindowRealmEntered);
|
||||
}
|
||||
let owner_async_scope = enter_subresource_owner_async_scope(
|
||||
&context_host,
|
||||
scope,
|
||||
pending.execution_context.dispatch_scope(),
|
||||
);
|
||||
let event_source = match &pending.continuation {
|
||||
PendingSubresourceContinuation::EventSource(event_source) => {
|
||||
v8::Local::new(scope, event_source)
|
||||
}
|
||||
_ => unreachable!("EventSource completion requires EventSource continuation"),
|
||||
};
|
||||
let request_handle = pending.info.network_request_handle;
|
||||
|
||||
match result {
|
||||
Err(error_text) => {
|
||||
let error_text = network_error_text.unwrap_or(error_text);
|
||||
let mut record = crate::types::SubresourceNetworkRecord::failure(
|
||||
pending.info.frame_id.clone(),
|
||||
pending.info.document_url.clone(),
|
||||
request_url,
|
||||
request_method,
|
||||
request_headers,
|
||||
request_body,
|
||||
SubresourceResourceType::EventSource,
|
||||
error_text,
|
||||
);
|
||||
if let Some(handle) = request_handle {
|
||||
record = record.with_request_handle(handle);
|
||||
}
|
||||
context_host
|
||||
.borrow_mut()
|
||||
.record_subresource_network(record);
|
||||
crate::network_host::fail_event_source_connection(
|
||||
scope,
|
||||
event_source,
|
||||
crate::network_host::EventSourceTerminalMode::Reconnect,
|
||||
);
|
||||
}
|
||||
Ok(response) => {
|
||||
let security_error = if !response.redirect_chain.is_empty() {
|
||||
document_connect_csp_redirect_failure_message(
|
||||
scope,
|
||||
&context_host,
|
||||
&pending,
|
||||
&response.final_url,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.or_else(|| {
|
||||
(!skip_fetch_security_validation)
|
||||
.then(|| {
|
||||
crate::network_host::validate_fetch_response_security_policy_with_body(
|
||||
&pending.request_origin,
|
||||
&response.head(),
|
||||
response.body_bytes(),
|
||||
pending.request_mode,
|
||||
pending.credentials_mode,
|
||||
pending.policy_context,
|
||||
)
|
||||
.err()
|
||||
})
|
||||
.flatten()
|
||||
});
|
||||
if let Some(error_text) = security_error {
|
||||
let mut record = crate::types::SubresourceNetworkRecord::failure(
|
||||
pending.info.frame_id.clone(),
|
||||
pending.info.document_url.clone(),
|
||||
request_url,
|
||||
request_method,
|
||||
request_headers,
|
||||
request_body,
|
||||
SubresourceResourceType::EventSource,
|
||||
error_text,
|
||||
);
|
||||
if let Some(handle) = request_handle {
|
||||
record = record.with_request_handle(handle);
|
||||
}
|
||||
context_host
|
||||
.borrow_mut()
|
||||
.record_subresource_network(record);
|
||||
crate::network_host::fail_event_source_connection(
|
||||
scope,
|
||||
event_source,
|
||||
crate::network_host::EventSourceTerminalMode::Close,
|
||||
);
|
||||
} else {
|
||||
let head = response.head();
|
||||
if let Some(handle) = request_handle {
|
||||
context_host
|
||||
.borrow_mut()
|
||||
.record_subresource_response_started(
|
||||
crate::types::SubresourceResponseStarted::new(
|
||||
handle,
|
||||
response.redirect_chain.clone(),
|
||||
response.final_url.clone(),
|
||||
response.status,
|
||||
response.headers.clone(),
|
||||
response.cookie_set_reports.clone(),
|
||||
)
|
||||
.with_status_text(response_status_text)
|
||||
.with_from_cache(response.from_cache)
|
||||
.with_negotiated_http_version(
|
||||
response.negotiated_http_version,
|
||||
)
|
||||
.with_network_request_headers(
|
||||
response
|
||||
.network_request_headers()
|
||||
.map(|headers| headers.to_vec()),
|
||||
),
|
||||
);
|
||||
}
|
||||
if let Some(error_text) =
|
||||
crate::network_host::event_source_response_error(&head)
|
||||
{
|
||||
if let Some(handle) = request_handle {
|
||||
context_host
|
||||
.borrow_mut()
|
||||
.record_subresource_body_finished(
|
||||
crate::types::SubresourceBodyFinished::failed(
|
||||
handle, error_text,
|
||||
),
|
||||
);
|
||||
}
|
||||
crate::network_host::fail_event_source_connection(
|
||||
scope,
|
||||
event_source,
|
||||
crate::network_host::EventSourceTerminalMode::Close,
|
||||
);
|
||||
} else {
|
||||
crate::network_host::open_event_source_connection(
|
||||
scope,
|
||||
event_source,
|
||||
&response.final_url,
|
||||
);
|
||||
let bytes = response.body_bytes();
|
||||
if let Some(handle) = request_handle
|
||||
&& !bytes.is_empty()
|
||||
{
|
||||
context_host.borrow_mut().record_subresource_data_received(
|
||||
crate::types::SubresourceDataReceived::new(
|
||||
handle,
|
||||
bytes.len(),
|
||||
bytes.len(),
|
||||
),
|
||||
);
|
||||
}
|
||||
let mut parser = crate::network_host::EventSourceParser::new(
|
||||
crate::network_host::event_source_last_event_id(
|
||||
scope,
|
||||
event_source,
|
||||
),
|
||||
crate::network_host::event_source_reconnect_delay_ms(
|
||||
scope,
|
||||
event_source,
|
||||
),
|
||||
);
|
||||
if crate::network_host::event_source_ready_state(scope, event_source)
|
||||
!= crate::network_host::EVENT_SOURCE_CLOSED
|
||||
{
|
||||
let messages = parser.push(bytes);
|
||||
dispatch_streaming_event_source_messages(
|
||||
&context_host,
|
||||
scope,
|
||||
event_source,
|
||||
request_handle,
|
||||
&messages,
|
||||
);
|
||||
}
|
||||
if crate::network_host::event_source_ready_state(scope, event_source)
|
||||
!= crate::network_host::EVENT_SOURCE_CLOSED
|
||||
{
|
||||
crate::network_host::update_event_source_stream_state(
|
||||
scope,
|
||||
event_source,
|
||||
parser.last_event_id(),
|
||||
parser.reconnect_delay_ms(),
|
||||
);
|
||||
}
|
||||
if let Some(handle) = request_handle {
|
||||
context_host.borrow_mut().record_subresource_body_finished(
|
||||
crate::types::SubresourceBodyFinished::ready_after_streaming(
|
||||
handle,
|
||||
SubresourceResponseBody::from_navigation_response(
|
||||
&response,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
if crate::network_host::event_source_ready_state(scope, event_source)
|
||||
!= crate::network_host::EVENT_SOURCE_CLOSED
|
||||
{
|
||||
crate::network_host::fail_event_source_connection(
|
||||
scope,
|
||||
event_source,
|
||||
crate::network_host::EventSourceTerminalMode::Reconnect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
defer_subresource_owner_async_scope(
|
||||
&context_host,
|
||||
scope,
|
||||
pending.execution_context.dispatch_scope(),
|
||||
owner_async_scope,
|
||||
);
|
||||
Ok(AsyncSubresourceFetchBodyActivity::WindowRealmEntered)
|
||||
})
|
||||
}
|
||||
pub(super) fn resolve_pending_subresource_fetch_body(
|
||||
&mut self,
|
||||
pending: PendingSubresourceFetchState,
|
||||
request_url: Url,
|
||||
request_method: String,
|
||||
request_headers: moli_fetch::RequestHeaders,
|
||||
request_body: Option<String>,
|
||||
response_status_text: Option<String>,
|
||||
skip_fetch_security_validation: bool,
|
||||
response_filter: Option<AsyncSubresourceFetchResponseFilter>,
|
||||
network_error_text: Option<String>,
|
||||
result: std::result::Result<crate::protocol_types::NavigationResponse, String>,
|
||||
) -> Result<AsyncSubresourceFetchBodyActivity> {
|
||||
self.resolve_pending_subresource_fetch_completion(
|
||||
pending,
|
||||
request_url,
|
||||
request_method,
|
||||
request_headers,
|
||||
request_body,
|
||||
response_status_text,
|
||||
skip_fetch_security_validation,
|
||||
response_filter,
|
||||
network_error_text,
|
||||
result.into(),
|
||||
)
|
||||
}
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn resolve_pending_subresource_fetch_completion(
|
||||
&mut self,
|
||||
mut pending: PendingSubresourceFetchState,
|
||||
request_url: Url,
|
||||
request_method: String,
|
||||
request_headers: moli_fetch::RequestHeaders,
|
||||
request_body: Option<String>,
|
||||
response_status_text: Option<String>,
|
||||
skip_fetch_security_validation: bool,
|
||||
response_filter: Option<AsyncSubresourceFetchResponseFilter>,
|
||||
network_error_text: Option<String>,
|
||||
completion_result: AsyncSubresourceFetchResult,
|
||||
) -> Result<AsyncSubresourceFetchBodyActivity> {
|
||||
let (supplied_parkable_image, result) = match completion_result {
|
||||
AsyncSubresourceFetchResult::Response(response) => (None, Ok(response)),
|
||||
AsyncSubresourceFetchResult::Image { response, encoded } => {
|
||||
(Some(encoded), Ok(response))
|
||||
}
|
||||
AsyncSubresourceFetchResult::Failure(error) => (None, Err(error)),
|
||||
};
|
||||
let trace_started = moli_trace::cdp_runtime_trace_enabled().then(Instant::now);
|
||||
let trace_fields = async_subresource_trace_fields_for_pending(
|
||||
"completion",
|
||||
pending.info.internal_id,
|
||||
&pending,
|
||||
);
|
||||
trace_async_subresource_stage(
|
||||
"async_subresource_resolve_pending_start",
|
||||
trace_fields,
|
||||
trace_started,
|
||||
);
|
||||
if pending.continuation.is_detached_window_fetch()
|
||||
|| pending.execution_context.is_window_network_only()
|
||||
{
|
||||
return self
|
||||
.resolve_network_only_subresource_fetch(
|
||||
pending,
|
||||
request_url,
|
||||
request_method,
|
||||
request_headers,
|
||||
request_body,
|
||||
response_status_text,
|
||||
skip_fetch_security_validation,
|
||||
network_error_text,
|
||||
result,
|
||||
)
|
||||
.map(|()| AsyncSubresourceFetchBodyActivity::NoWindowRealmEntered);
|
||||
}
|
||||
if !window_subresource_owner_is_current(&self._context_host, &pending) {
|
||||
tracing::debug!(
|
||||
internal_id = pending.info.internal_id,
|
||||
owner = ?pending.execution_context.window_request_target().map(crate::native_bridge::WindowTaskTarget::owner),
|
||||
"discarded subresource completion for retired Window execution context"
|
||||
);
|
||||
return Ok(AsyncSubresourceFetchBodyActivity::NoWindowRealmEntered);
|
||||
}
|
||||
if pending.continuation.is_window_event_source() {
|
||||
return self.resolve_pending_event_source_fetch(
|
||||
pending,
|
||||
request_url,
|
||||
request_method,
|
||||
request_headers,
|
||||
request_body,
|
||||
response_status_text,
|
||||
skip_fetch_security_validation,
|
||||
network_error_text,
|
||||
result,
|
||||
);
|
||||
}
|
||||
self._context_host
|
||||
.borrow_mut()
|
||||
.record_deferred_pending_subresource_request_started(&mut pending);
|
||||
let context_host = self._context_host.clone();
|
||||
let mut completed_web_font = None;
|
||||
let result = self.renderer_document_isolate.with_entered_renderer_document_isolate(|isolate| {
|
||||
let scope = pin!(v8::HandleScope::new(isolate));
|
||||
let scope = &mut scope.init();
|
||||
let context = v8::Local::new(
|
||||
scope,
|
||||
pending
|
||||
.execution_context
|
||||
.context_global()
|
||||
.expect("active subresource completion must retain its V8 context"),
|
||||
);
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
if !window_subresource_realm_is_current(&context_host, scope, &pending) {
|
||||
tracing::debug!(
|
||||
internal_id = pending.info.internal_id,
|
||||
expected_realm = ?pending.execution_context.realm_token(),
|
||||
"discarded subresource completion for retired V8 realm"
|
||||
);
|
||||
return Ok(AsyncSubresourceFetchBodyActivity::NoWindowRealmEntered);
|
||||
}
|
||||
trace_async_subresource_stage(
|
||||
"async_subresource_context_entered",
|
||||
trace_fields,
|
||||
trace_started,
|
||||
);
|
||||
let owner_async_scope =
|
||||
enter_subresource_owner_async_scope(
|
||||
&context_host,
|
||||
scope,
|
||||
pending.execution_context.dispatch_scope(),
|
||||
);
|
||||
|
||||
let security_started = moli_trace::cdp_runtime_trace_enabled().then(Instant::now);
|
||||
let mut opaque_response_blocked = false;
|
||||
let result = result.and_then(|response| {
|
||||
if !response.redirect_chain.is_empty()
|
||||
&& let Some(message) = document_connect_csp_redirect_failure_message(
|
||||
scope,
|
||||
&context_host,
|
||||
&pending,
|
||||
&response.final_url,
|
||||
)
|
||||
{
|
||||
return Err(message);
|
||||
}
|
||||
if !skip_fetch_security_validation
|
||||
&& matches!(
|
||||
pending.info.resource_type,
|
||||
SubresourceResourceType::Audio
|
||||
| SubresourceResourceType::Fetch
|
||||
| SubresourceResourceType::Font
|
||||
| SubresourceResourceType::Image
|
||||
| SubresourceResourceType::Media
|
||||
| SubresourceResourceType::TextTrack
|
||||
| SubresourceResourceType::Video
|
||||
| SubresourceResourceType::Xhr
|
||||
) {
|
||||
let supplied_snapshot = supplied_parkable_image
|
||||
.as_ref()
|
||||
.map(|image| {
|
||||
image.snapshot().map_err(|error| {
|
||||
format!("failed to read image response bytes: {error}")
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
let response_body = supplied_snapshot
|
||||
.as_ref()
|
||||
.map_or_else(|| response.body_bytes(), AsRef::as_ref);
|
||||
let validation = crate::network_host::validate_fetch_response_security_policy_with_body_classified(
|
||||
&pending.request_origin,
|
||||
&response.head(),
|
||||
response_body,
|
||||
pending.request_mode,
|
||||
pending.credentials_mode,
|
||||
pending.policy_context,
|
||||
);
|
||||
match validation {
|
||||
Ok(()) => {}
|
||||
Err(crate::network_host::FetchResponseSecurityViolation::OpaqueResponseBlocked(_))
|
||||
if pending.continuation.is_window_fetch() =>
|
||||
{
|
||||
opaque_response_blocked = true;
|
||||
}
|
||||
Err(violation) => return Err(violation.into_message()),
|
||||
}
|
||||
}
|
||||
Ok(response)
|
||||
});
|
||||
trace_async_subresource_stage(
|
||||
"async_subresource_security_checked",
|
||||
trace_fields,
|
||||
security_started,
|
||||
);
|
||||
|
||||
let request_initiator_type = pending.continuation.request_initiator_type();
|
||||
match result {
|
||||
Ok(mut response) => {
|
||||
let response_status = response.status;
|
||||
let parkable_image = (!opaque_response_blocked
|
||||
&& pending.info.resource_type == SubresourceResourceType::Image)
|
||||
.then(|| {
|
||||
supplied_parkable_image.clone().unwrap_or_else(|| {
|
||||
let runner = pending.load.task_runner();
|
||||
pending
|
||||
.load
|
||||
.request_client()
|
||||
.parkable_image_manager(&runner)
|
||||
.from_frozen_bytes(response.take_body_bytes())
|
||||
})
|
||||
});
|
||||
let request_cookie_report = response
|
||||
.request_cookie_report
|
||||
.clone()
|
||||
.or_else(|| pending.info.request_cookie_report.clone());
|
||||
let record_started =
|
||||
moli_trace::cdp_runtime_trace_enabled().then(Instant::now);
|
||||
let mut network_record = if opaque_response_blocked {
|
||||
crate::types::SubresourceNetworkRecord::failure(
|
||||
pending.info.frame_id.clone(),
|
||||
pending.info.document_url.clone(),
|
||||
request_url.clone(),
|
||||
request_method,
|
||||
request_headers,
|
||||
request_body,
|
||||
pending.info.resource_type,
|
||||
crate::network_host::ABORTED_ERROR_TEXT.to_owned(),
|
||||
)
|
||||
.with_request_initiator_type(request_initiator_type)
|
||||
.with_request_body_bytes(pending.info.request_body_bytes.clone())
|
||||
} else {
|
||||
crate::types::SubresourceNetworkRecord::success_with_body(
|
||||
pending.info.frame_id.clone(),
|
||||
pending.info.document_url.clone(),
|
||||
request_url.clone(),
|
||||
request_method,
|
||||
request_headers,
|
||||
request_body,
|
||||
pending.info.resource_type,
|
||||
request_cookie_report,
|
||||
response.redirect_chain.clone().into_iter().collect(),
|
||||
response.final_url.clone(),
|
||||
response.status,
|
||||
response.headers.clone(),
|
||||
parkable_image.as_ref().map_or_else(
|
||||
|| SubresourceResponseBody::from_navigation_response(&response),
|
||||
|image| {
|
||||
SubresourceResponseBody::from_parkable_image(image.clone())
|
||||
},
|
||||
),
|
||||
response.cookie_set_reports.clone(),
|
||||
)
|
||||
.with_from_cache(response.from_cache)
|
||||
.with_negotiated_http_version(response.negotiated_http_version)
|
||||
.with_network_request_headers(
|
||||
response.network_request_headers().map(|headers| headers.to_vec()),
|
||||
)
|
||||
.with_request_initiator_type(request_initiator_type)
|
||||
.with_request_body_bytes(pending.info.request_body_bytes.clone())
|
||||
};
|
||||
if !opaque_response_blocked
|
||||
&& let Some(status_text) = response_status_text.as_deref()
|
||||
{
|
||||
network_record =
|
||||
network_record.with_response_status_text(status_text);
|
||||
}
|
||||
if let Some(handle) = pending.info.network_request_handle {
|
||||
network_record = network_record.with_request_handle(handle);
|
||||
}
|
||||
context_host
|
||||
.borrow_mut()
|
||||
.record_subresource_network(network_record);
|
||||
trace_async_subresource_stage(
|
||||
"async_subresource_network_recorded",
|
||||
trace_fields,
|
||||
record_started,
|
||||
);
|
||||
let mut observable_response = response;
|
||||
if matches!(
|
||||
pending.info.resource_type,
|
||||
SubresourceResourceType::Fetch | SubresourceResourceType::Xhr
|
||||
) && !response_filter.is_some_and(|filter| filter.is_readable()) {
|
||||
observable_response.headers =
|
||||
crate::network_host::filter_cors_exposed_response_headers(
|
||||
&pending.request_origin,
|
||||
&observable_response.head(),
|
||||
pending.credentials_mode,
|
||||
);
|
||||
}
|
||||
let continuation_started =
|
||||
moli_trace::cdp_runtime_trace_enabled().then(Instant::now);
|
||||
match pending.continuation {
|
||||
PendingSubresourceContinuation::Fetch(fetch) => {
|
||||
let resolver = fetch
|
||||
.into_resolver()
|
||||
.expect("detached keepalive completion is handled before V8 entry");
|
||||
let resolver = v8::Local::new(scope, &resolver);
|
||||
let (head, body) = observable_response.into_body();
|
||||
let body = if opaque_response_blocked {
|
||||
moli_fetch::ResponseBody::materialized_bytes(Vec::new())
|
||||
} else {
|
||||
body
|
||||
};
|
||||
let response_filter = opaque_response_blocked
|
||||
.then_some(AsyncSubresourceFetchResponseFilter::Opaque)
|
||||
.or(response_filter);
|
||||
let response_obj =
|
||||
crate::network_host::build_fetch_response_object_from_body_source_for_request_mode_with_filter(
|
||||
scope,
|
||||
&pending.request_origin,
|
||||
pending.request_mode,
|
||||
head,
|
||||
body,
|
||||
response_filter,
|
||||
);
|
||||
if let Some(status_text) = response_status_text.as_deref() {
|
||||
crate::network_host::set_response_slot_string(
|
||||
scope,
|
||||
response_obj,
|
||||
crate::network_host::RESPONSE_STATUS_TEXT_SLOT,
|
||||
status_text,
|
||||
);
|
||||
}
|
||||
resolver.resolve(scope, response_obj.into());
|
||||
}
|
||||
PendingSubresourceContinuation::Xhr(xhr) => {
|
||||
crate::context_bootstrap::record_resource_performance_entry(
|
||||
scope,
|
||||
crate::context_bootstrap::ResourcePerformanceEntry::from_network_response(
|
||||
pending.info.url.as_str(),
|
||||
"xmlhttprequest",
|
||||
None,
|
||||
&observable_response,
|
||||
),
|
||||
);
|
||||
let xhr = v8::Local::new(scope, &xhr);
|
||||
let (head, body) = observable_response.into_body();
|
||||
crate::network_host::apply_xhr_response_body_source_with_status_text(
|
||||
scope,
|
||||
xhr,
|
||||
head,
|
||||
body,
|
||||
response_status_text.as_deref(),
|
||||
);
|
||||
}
|
||||
PendingSubresourceContinuation::Image {
|
||||
image_handle,
|
||||
sequence,
|
||||
..
|
||||
} => {
|
||||
let encoded = parkable_image
|
||||
.as_ref()
|
||||
.expect("an accepted image response must be parkable");
|
||||
apply_image_subresource_terminal(
|
||||
scope,
|
||||
&context_host,
|
||||
image_handle,
|
||||
sequence,
|
||||
pending.info.internal_id,
|
||||
&pending.info.url,
|
||||
ImageSubresourceTerminal::Response {
|
||||
response: &observable_response,
|
||||
encoded,
|
||||
},
|
||||
)
|
||||
}
|
||||
PendingSubresourceContinuation::Media {
|
||||
media_handle,
|
||||
sequence,
|
||||
} => apply_media_subresource_terminal(
|
||||
scope,
|
||||
&context_host,
|
||||
media_handle,
|
||||
sequence,
|
||||
pending.info.internal_id,
|
||||
crate::network_host::media_response_status_is_successful(
|
||||
response_status,
|
||||
),
|
||||
),
|
||||
PendingSubresourceContinuation::TextTrack {
|
||||
track_handle,
|
||||
sequence,
|
||||
} => apply_text_track_subresource_terminal(
|
||||
scope,
|
||||
&context_host,
|
||||
track_handle,
|
||||
sequence,
|
||||
pending.info.internal_id,
|
||||
crate::network_host::text_track_response_result(
|
||||
response_status,
|
||||
observable_response.body_text(),
|
||||
),
|
||||
),
|
||||
PendingSubresourceContinuation::StylesheetSubresource {
|
||||
binding,
|
||||
web_font,
|
||||
css_image,
|
||||
} => {
|
||||
if let Some(identity) = css_image.as_ref() {
|
||||
let descriptor = crate::network_host::image_response_descriptor_from_parkable(
|
||||
&observable_response,
|
||||
parkable_image
|
||||
.as_ref()
|
||||
.expect("a CSS image response must be parkable"),
|
||||
);
|
||||
let _ = context_host
|
||||
.borrow_mut()
|
||||
.complete_stylesheet_css_image_response(
|
||||
identity,
|
||||
descriptor,
|
||||
parkable_image
|
||||
.as_ref()
|
||||
.expect("a CSS image response must be parkable")
|
||||
.clone(),
|
||||
);
|
||||
}
|
||||
if binding.child_handle().is_none() {
|
||||
completed_web_font = web_font.map(|font| {
|
||||
if (200..=299).contains(&response_status)
|
||||
&& !opaque_response_blocked
|
||||
{
|
||||
crate::css_resource_urls::CompletedStylesheetWebFont::response(
|
||||
font,
|
||||
observable_response.clone_body_bytes(),
|
||||
)
|
||||
} else {
|
||||
crate::css_resource_urls::CompletedStylesheetWebFont::failure(
|
||||
font,
|
||||
)
|
||||
}
|
||||
});
|
||||
}
|
||||
apply_stylesheet_subresource_terminal(&context_host, binding);
|
||||
}
|
||||
PendingSubresourceContinuation::Beacon
|
||||
| PendingSubresourceContinuation::CspReport { .. }
|
||||
| PendingSubresourceContinuation::EventSource(_)
|
||||
| PendingSubresourceContinuation::WebSocket(_)
|
||||
| PendingSubresourceContinuation::WorkerFetch { .. }
|
||||
| PendingSubresourceContinuation::WorkerXhr { .. }
|
||||
| PendingSubresourceContinuation::WorkerCspReport { .. }
|
||||
| PendingSubresourceContinuation::SharedWorkerFetch { .. }
|
||||
| PendingSubresourceContinuation::SharedWorkerXhr { .. }
|
||||
| PendingSubresourceContinuation::SharedWorkerCspReport { .. } => {}
|
||||
}
|
||||
trace_async_subresource_stage(
|
||||
"async_subresource_continuation_delivered",
|
||||
trace_fields,
|
||||
continuation_started,
|
||||
);
|
||||
}
|
||||
Err(error_text) => {
|
||||
let network_error_text = network_error_text
|
||||
.as_deref()
|
||||
.or_else(|| {
|
||||
crate::network_host::is_cors_policy_failure_message(&error_text)
|
||||
.then_some(crate::network_host::FAILED_ERROR_TEXT)
|
||||
})
|
||||
.unwrap_or(&error_text);
|
||||
let record_started =
|
||||
moli_trace::cdp_runtime_trace_enabled().then(Instant::now);
|
||||
let mut network_record = crate::types::SubresourceNetworkRecord::failure(
|
||||
pending.info.frame_id.clone(),
|
||||
pending.info.document_url.clone(),
|
||||
request_url,
|
||||
request_method,
|
||||
request_headers,
|
||||
request_body,
|
||||
pending.info.resource_type,
|
||||
network_error_text.to_owned(),
|
||||
)
|
||||
.with_request_initiator_type(request_initiator_type);
|
||||
if let Some(handle) = pending.info.network_request_handle {
|
||||
network_record = network_record.with_request_handle(handle);
|
||||
}
|
||||
context_host
|
||||
.borrow_mut()
|
||||
.record_subresource_network(network_record);
|
||||
trace_async_subresource_stage(
|
||||
"async_subresource_network_recorded",
|
||||
trace_fields,
|
||||
record_started,
|
||||
);
|
||||
let continuation_started =
|
||||
moli_trace::cdp_runtime_trace_enabled().then(Instant::now);
|
||||
match pending.continuation {
|
||||
PendingSubresourceContinuation::Fetch(fetch) => {
|
||||
let resolver = fetch
|
||||
.into_resolver()
|
||||
.expect("detached keepalive failure is handled before V8 entry");
|
||||
let resolver = v8::Local::new(scope, &resolver);
|
||||
let exception = v8_string(scope, &error_text)
|
||||
.map(|message| v8::Exception::type_error(scope, message))
|
||||
.unwrap_or_else(|| v8::undefined(scope).into());
|
||||
resolver.reject(scope, exception);
|
||||
}
|
||||
PendingSubresourceContinuation::Xhr(xhr) => {
|
||||
let xhr = v8::Local::new(scope, &xhr);
|
||||
crate::network_host::apply_xhr_failure(scope, xhr);
|
||||
}
|
||||
PendingSubresourceContinuation::Image {
|
||||
image_handle,
|
||||
sequence,
|
||||
..
|
||||
} => apply_image_subresource_terminal(
|
||||
scope,
|
||||
&context_host,
|
||||
image_handle,
|
||||
sequence,
|
||||
pending.info.internal_id,
|
||||
&pending.info.url,
|
||||
ImageSubresourceTerminal::Failure,
|
||||
),
|
||||
PendingSubresourceContinuation::Media {
|
||||
media_handle,
|
||||
sequence,
|
||||
} => apply_media_subresource_terminal(
|
||||
scope,
|
||||
&context_host,
|
||||
media_handle,
|
||||
sequence,
|
||||
pending.info.internal_id,
|
||||
false,
|
||||
),
|
||||
PendingSubresourceContinuation::TextTrack {
|
||||
track_handle,
|
||||
sequence,
|
||||
} => apply_text_track_subresource_terminal(
|
||||
scope,
|
||||
&context_host,
|
||||
track_handle,
|
||||
sequence,
|
||||
pending.info.internal_id,
|
||||
Err(error_text.clone()),
|
||||
),
|
||||
PendingSubresourceContinuation::StylesheetSubresource {
|
||||
binding,
|
||||
web_font,
|
||||
css_image,
|
||||
} => {
|
||||
if let Some(identity) = css_image.as_ref() {
|
||||
let _ = context_host
|
||||
.borrow_mut()
|
||||
.fail_stylesheet_css_image(identity);
|
||||
}
|
||||
if binding.child_handle().is_none() {
|
||||
completed_web_font = web_font.map(
|
||||
crate::css_resource_urls::CompletedStylesheetWebFont::failure,
|
||||
);
|
||||
}
|
||||
apply_stylesheet_subresource_terminal(&context_host, binding);
|
||||
}
|
||||
PendingSubresourceContinuation::Beacon
|
||||
| PendingSubresourceContinuation::CspReport { .. }
|
||||
| PendingSubresourceContinuation::EventSource(_)
|
||||
| PendingSubresourceContinuation::WebSocket(_)
|
||||
| PendingSubresourceContinuation::WorkerFetch { .. }
|
||||
| PendingSubresourceContinuation::WorkerXhr { .. }
|
||||
| PendingSubresourceContinuation::WorkerCspReport { .. }
|
||||
| PendingSubresourceContinuation::SharedWorkerFetch { .. }
|
||||
| PendingSubresourceContinuation::SharedWorkerXhr { .. }
|
||||
| PendingSubresourceContinuation::SharedWorkerCspReport { .. } => {}
|
||||
}
|
||||
trace_async_subresource_stage(
|
||||
"async_subresource_continuation_delivered",
|
||||
trace_fields,
|
||||
continuation_started,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
defer_subresource_owner_async_scope(
|
||||
&context_host,
|
||||
scope,
|
||||
pending.execution_context.dispatch_scope(),
|
||||
owner_async_scope,
|
||||
);
|
||||
Ok(AsyncSubresourceFetchBodyActivity::WindowRealmEntered)
|
||||
});
|
||||
if let Some(web_font) = completed_web_font {
|
||||
self.complete_document_web_font(web_font);
|
||||
}
|
||||
trace_async_subresource_stage(
|
||||
"async_subresource_resolve_pending_done",
|
||||
trace_fields,
|
||||
trace_started,
|
||||
);
|
||||
result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
use super::*;
|
||||
|
||||
impl ScriptVm {
|
||||
pub(crate) fn should_intercept_parser_script_source_fetch(
|
||||
&self,
|
||||
script: &crate::planning::PreparedScript,
|
||||
) -> bool {
|
||||
script.source_kind == crate::types::ScriptSourceKind::External
|
||||
&& matches!(script.url.scheme(), "http" | "https")
|
||||
&& self
|
||||
._context_host
|
||||
.borrow()
|
||||
.should_intercept_subresource(SubresourceResourceType::Script)
|
||||
}
|
||||
pub(crate) fn start_parser_script_source_fetch_interception(
|
||||
&mut self,
|
||||
script: crate::planning::PreparedScript,
|
||||
request_client: ResourceRequestClient,
|
||||
task_runner: crate::network::RendererResourceTaskRunner,
|
||||
browser_context_runtime: crate::runtime::RendererBrowserContextRuntime,
|
||||
document_character_set: Option<String>,
|
||||
) -> crate::planning::SharedScriptSourceLoad {
|
||||
// Main-parser source completion is routed by the exact parser
|
||||
// continuation registered on the returned load. The interception
|
||||
// request itself is frozen into this Page turn's concrete output
|
||||
// journal; it is never parked in browser-global state for a later
|
||||
// protocol snapshot to rediscover.
|
||||
let (load, completer) =
|
||||
crate::planning::SharedScriptSourceLoad::pending_with_owner_wake(None);
|
||||
let (info, continuation) = browser_context_runtime.prepare_detached_parser_script_fetch(
|
||||
PendingSubresourceFetchInfo {
|
||||
internal_id: 0,
|
||||
network_request_handle: None,
|
||||
frame_id: self.root_frame_id.clone(),
|
||||
document_url: script.initiator_url.clone(),
|
||||
url: script.url.clone(),
|
||||
websocket_socket_id: None,
|
||||
method: "GET".to_owned(),
|
||||
request_headers: Default::default(),
|
||||
request_body: None,
|
||||
request_body_bytes: None,
|
||||
resource_type: SubresourceResourceType::Script,
|
||||
request_cookie_report: None,
|
||||
},
|
||||
script,
|
||||
self.current_main_document_resource_loader()
|
||||
.expect("parser interception requires its Document authority")
|
||||
.fetch_context()
|
||||
.request_origin(),
|
||||
request_client,
|
||||
task_runner,
|
||||
document_character_set,
|
||||
completer,
|
||||
);
|
||||
let source_document = self
|
||||
._context_host
|
||||
.borrow()
|
||||
.root_document_lifecycle_identity()
|
||||
.expect("parser fetch interception requires an active root Document");
|
||||
let appended = self._context_host.borrow().append_live_turn_owner_action(
|
||||
crate::runtime::RendererOwnerAction::DetachedParserScriptFetchPause {
|
||||
source_document,
|
||||
info: Box::new(info),
|
||||
continuation,
|
||||
},
|
||||
);
|
||||
assert!(
|
||||
appended,
|
||||
"parser fetch interception requires a concrete Page output journal"
|
||||
);
|
||||
load
|
||||
}
|
||||
pub(super) fn continue_worker_owned_fetch(
|
||||
&self,
|
||||
target: WorkerOwnedFetchTarget,
|
||||
request: crate::worker::WorkerPendingFetchContinue,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedFetchTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.continue_worker_fetch(worker_id, request),
|
||||
WorkerOwnedFetchTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.continue_shared_worker_fetch(instance_id, request),
|
||||
}
|
||||
}
|
||||
pub(super) fn continue_worker_owned_xhr(
|
||||
&self,
|
||||
target: WorkerOwnedXhrTarget,
|
||||
request: crate::worker::WorkerPendingXhrContinue,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedXhrTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.continue_worker_xhr(worker_id, request),
|
||||
WorkerOwnedXhrTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.continue_shared_worker_xhr(instance_id, request),
|
||||
}
|
||||
}
|
||||
pub(super) fn continue_worker_owned_csp_report(
|
||||
&self,
|
||||
target: WorkerOwnedCspReportTarget,
|
||||
request: crate::worker::WorkerPendingFetchContinue,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedCspReportTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.continue_worker_csp_report(worker_id, request),
|
||||
WorkerOwnedCspReportTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.continue_shared_worker_csp_report(instance_id, request),
|
||||
}
|
||||
}
|
||||
pub(super) fn fail_worker_owned_fetch(
|
||||
&self,
|
||||
target: WorkerOwnedFetchTarget,
|
||||
request: crate::worker::WorkerPendingFetchContinue,
|
||||
error_text: String,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedFetchTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_worker_fetch(worker_id, request, error_text),
|
||||
WorkerOwnedFetchTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_shared_worker_fetch(instance_id, request, error_text),
|
||||
}
|
||||
}
|
||||
pub(super) fn fail_worker_owned_xhr(
|
||||
&self,
|
||||
target: WorkerOwnedXhrTarget,
|
||||
request: crate::worker::WorkerPendingXhrContinue,
|
||||
error_text: String,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedXhrTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_worker_xhr(worker_id, request, error_text),
|
||||
WorkerOwnedXhrTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_shared_worker_xhr(instance_id, request, error_text),
|
||||
}
|
||||
}
|
||||
pub(super) fn fail_worker_owned_csp_report(
|
||||
&self,
|
||||
target: WorkerOwnedCspReportTarget,
|
||||
request: crate::worker::WorkerPendingFetchContinue,
|
||||
error_text: String,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedCspReportTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_worker_csp_report(worker_id, request, error_text),
|
||||
WorkerOwnedCspReportTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_shared_worker_csp_report(instance_id, request, error_text),
|
||||
}
|
||||
}
|
||||
pub(super) fn fail_worker_owned_fetch_auth(
|
||||
&self,
|
||||
target: WorkerOwnedFetchTarget,
|
||||
request: crate::worker::WorkerPendingFetchContinue,
|
||||
error_text: String,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedFetchTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_worker_fetch_auth(worker_id, request, error_text),
|
||||
WorkerOwnedFetchTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_shared_worker_fetch_auth(instance_id, request, error_text),
|
||||
}
|
||||
}
|
||||
pub(super) fn fail_worker_owned_xhr_auth(
|
||||
&self,
|
||||
target: WorkerOwnedXhrTarget,
|
||||
request: crate::worker::WorkerPendingXhrContinue,
|
||||
error_text: String,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedXhrTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_worker_xhr_auth(worker_id, request, error_text),
|
||||
WorkerOwnedXhrTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_shared_worker_xhr_auth(instance_id, request, error_text),
|
||||
}
|
||||
}
|
||||
pub(super) fn continue_worker_owned_fetch_response(
|
||||
&self,
|
||||
target: WorkerOwnedFetchTarget,
|
||||
request: crate::worker::WorkerPendingFetchContinue,
|
||||
response_code: Option<u16>,
|
||||
response_headers: Option<Vec<(String, Vec<u8>)>>,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedFetchTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.continue_worker_fetch_response(
|
||||
worker_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
),
|
||||
WorkerOwnedFetchTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.continue_shared_worker_fetch_response(
|
||||
instance_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
),
|
||||
}
|
||||
}
|
||||
pub(super) fn continue_worker_owned_xhr_response(
|
||||
&self,
|
||||
target: WorkerOwnedXhrTarget,
|
||||
request: crate::worker::WorkerPendingXhrContinue,
|
||||
response_code: Option<u16>,
|
||||
response_headers: Option<Vec<(String, Vec<u8>)>>,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedXhrTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.continue_worker_xhr_response(worker_id, request, response_code, response_headers),
|
||||
WorkerOwnedXhrTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.continue_shared_worker_xhr_response(
|
||||
instance_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
),
|
||||
}
|
||||
}
|
||||
pub(super) fn fail_worker_owned_fetch_response(
|
||||
&self,
|
||||
target: WorkerOwnedFetchTarget,
|
||||
request: crate::worker::WorkerPendingFetchContinue,
|
||||
error_text: String,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedFetchTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_worker_fetch_response(worker_id, request, error_text),
|
||||
WorkerOwnedFetchTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_shared_worker_fetch_response(instance_id, request, error_text),
|
||||
}
|
||||
}
|
||||
pub(super) fn fail_worker_owned_xhr_response(
|
||||
&self,
|
||||
target: WorkerOwnedXhrTarget,
|
||||
request: crate::worker::WorkerPendingXhrContinue,
|
||||
error_text: String,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedXhrTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_worker_xhr_response(worker_id, request, error_text),
|
||||
WorkerOwnedXhrTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fail_shared_worker_xhr_response(instance_id, request, error_text),
|
||||
}
|
||||
}
|
||||
pub(super) fn fulfill_worker_owned_fetch(
|
||||
&self,
|
||||
target: WorkerOwnedFetchTarget,
|
||||
request: crate::worker::WorkerPendingFetchContinue,
|
||||
response_code: u16,
|
||||
response_headers: Vec<(String, Vec<u8>)>,
|
||||
response_body: RendererSyntheticResponseBody,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedFetchTarget::Dedicated { worker_id, .. } => {
|
||||
self._context_host.borrow_mut().fulfill_worker_fetch(
|
||||
worker_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
response_body,
|
||||
)
|
||||
}
|
||||
WorkerOwnedFetchTarget::Shared { instance_id, .. } => {
|
||||
self._context_host.borrow_mut().fulfill_shared_worker_fetch(
|
||||
instance_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
response_body,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(super) fn fulfill_worker_owned_xhr(
|
||||
&self,
|
||||
target: WorkerOwnedXhrTarget,
|
||||
request: crate::worker::WorkerPendingXhrContinue,
|
||||
response_code: u16,
|
||||
response_headers: Vec<(String, Vec<u8>)>,
|
||||
response_body: RendererSyntheticResponseBody,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedXhrTarget::Dedicated { worker_id, .. } => {
|
||||
self._context_host.borrow_mut().fulfill_worker_xhr(
|
||||
worker_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
response_body,
|
||||
)
|
||||
}
|
||||
WorkerOwnedXhrTarget::Shared { instance_id, .. } => {
|
||||
self._context_host.borrow_mut().fulfill_shared_worker_xhr(
|
||||
instance_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
response_body,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(super) fn fulfill_worker_owned_csp_report(
|
||||
&self,
|
||||
target: WorkerOwnedCspReportTarget,
|
||||
request: crate::worker::WorkerPendingFetchContinue,
|
||||
response_code: u16,
|
||||
response_headers: Vec<(String, Vec<u8>)>,
|
||||
response_body: RendererSyntheticResponseBody,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedCspReportTarget::Dedicated { worker_id, .. } => {
|
||||
self._context_host.borrow_mut().fulfill_worker_csp_report(
|
||||
worker_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
response_body,
|
||||
)
|
||||
}
|
||||
WorkerOwnedCspReportTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fulfill_shared_worker_csp_report(
|
||||
instance_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
response_body,
|
||||
),
|
||||
}
|
||||
}
|
||||
pub(super) fn fulfill_worker_owned_fetch_response(
|
||||
&self,
|
||||
target: WorkerOwnedFetchTarget,
|
||||
request: crate::worker::WorkerPendingFetchContinue,
|
||||
response_code: u16,
|
||||
response_headers: Vec<(String, Vec<u8>)>,
|
||||
response_body: RendererSyntheticResponseBody,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedFetchTarget::Dedicated { worker_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fulfill_worker_fetch_response(
|
||||
worker_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
response_body,
|
||||
),
|
||||
WorkerOwnedFetchTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fulfill_shared_worker_fetch_response(
|
||||
instance_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
response_body,
|
||||
),
|
||||
}
|
||||
}
|
||||
pub(super) fn fulfill_worker_owned_xhr_response(
|
||||
&self,
|
||||
target: WorkerOwnedXhrTarget,
|
||||
request: crate::worker::WorkerPendingXhrContinue,
|
||||
response_code: u16,
|
||||
response_headers: Vec<(String, Vec<u8>)>,
|
||||
response_body: RendererSyntheticResponseBody,
|
||||
) -> bool {
|
||||
match target {
|
||||
WorkerOwnedXhrTarget::Dedicated { worker_id, .. } => {
|
||||
self._context_host.borrow_mut().fulfill_worker_xhr_response(
|
||||
worker_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
response_body,
|
||||
)
|
||||
}
|
||||
WorkerOwnedXhrTarget::Shared { instance_id, .. } => self
|
||||
._context_host
|
||||
.borrow_mut()
|
||||
.fulfill_shared_worker_xhr_response(
|
||||
instance_id,
|
||||
request,
|
||||
response_code,
|
||||
response_headers,
|
||||
response_body,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user