mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
script: Move navigation fetching to the ScriptThread
(#34919)
This allows reusing the asynchrnous fetch mechanism that we use for page resources and is likely a step toward removing the `FetchThread`. Benefits: - Reduces IPC traffic during navigation. Now instead of bouncing between the constellation and the `ScriptThread` responses are sent directly to the `ScriptThread`. - Allows cancelling loads after redirects, which was not possible before. There is the question of what to do when a redirect is cross-origin (#23037). This currently isn't handled properly as the `Constellation` sends data to the same `Pipeline` that initiated the load. This change doesn't fix this issue, but does make it more possible for the `ScriptThread` to shut down the pipeline and ask the `Constellation` to replace it with a new one. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
73c0701c83
commit
fbd77b4524
16 changed files with 420 additions and 460 deletions
|
@ -129,9 +129,9 @@ use keyboard_types::{CompositionEvent, KeyboardEvent};
|
|||
use log::{debug, error, info, trace, warn};
|
||||
use media::{GLPlayerThreads, WindowGLContext};
|
||||
use net_traits::pub_domains::reg_host;
|
||||
use net_traits::request::{Referrer, RequestBuilder};
|
||||
use net_traits::request::Referrer;
|
||||
use net_traits::storage_thread::{StorageThreadMsg, StorageType};
|
||||
use net_traits::{self, FetchResponseMsg, IpcSend, ReferrerPolicy, ResourceThreads};
|
||||
use net_traits::{self, IpcSend, ReferrerPolicy, ResourceThreads};
|
||||
use profile_traits::{mem, time};
|
||||
use script_layout_interface::{LayoutFactory, ScriptThreadFactory};
|
||||
use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent};
|
||||
|
@ -166,7 +166,6 @@ use crate::browsingcontext::{
|
|||
NewBrowsingContextInfo,
|
||||
};
|
||||
use crate::event_loop::EventLoop;
|
||||
use crate::network_listener::NetworkListener;
|
||||
use crate::pipeline::{InitialPipelineState, Pipeline};
|
||||
use crate::serviceworker::ServiceWorkerUnprivilegedContent;
|
||||
use crate::session_history::{
|
||||
|
@ -317,12 +316,6 @@ pub struct Constellation<STF, SWF> {
|
|||
/// This is the constellation's view of `layout_sender`.
|
||||
layout_receiver: Receiver<Result<FromLayoutMsg, IpcError>>,
|
||||
|
||||
/// A channel for network listener to send messages to the constellation.
|
||||
network_listener_sender: Sender<(PipelineId, FetchResponseMsg)>,
|
||||
|
||||
/// A channel for the constellation to receive messages from network listener.
|
||||
network_listener_receiver: Receiver<(PipelineId, FetchResponseMsg)>,
|
||||
|
||||
/// A channel for the constellation to receive messages from the compositor thread.
|
||||
compositor_receiver: Receiver<FromCompositorMsg>,
|
||||
|
||||
|
@ -685,8 +678,6 @@ where
|
|||
layout_ipc_receiver,
|
||||
);
|
||||
|
||||
let (network_listener_sender, network_listener_receiver) = unbounded();
|
||||
|
||||
let swmanager_receiver =
|
||||
route_ipc_receiver_to_new_crossbeam_receiver_preserving_errors(
|
||||
swmanager_ipc_receiver,
|
||||
|
@ -715,8 +706,6 @@ where
|
|||
compositor_receiver,
|
||||
layout_factory,
|
||||
layout_receiver,
|
||||
network_listener_sender,
|
||||
network_listener_receiver,
|
||||
embedder_proxy: state.embedder_proxy,
|
||||
compositor_proxy: state.compositor_proxy,
|
||||
webviews: WebViewManager::default(),
|
||||
|
@ -1156,15 +1145,12 @@ where
|
|||
)]
|
||||
fn handle_request(&mut self) {
|
||||
#[derive(Debug)]
|
||||
// FIXME: https://github.com/servo/servo/issues/34591
|
||||
#[expect(clippy::large_enum_variant)]
|
||||
enum Request {
|
||||
PipelineNamespace(PipelineNamespaceRequest),
|
||||
Script((PipelineId, FromScriptMsg)),
|
||||
BackgroundHangMonitor(HangMonitorAlert),
|
||||
Compositor(FromCompositorMsg),
|
||||
Layout(FromLayoutMsg),
|
||||
NetworkListener((PipelineId, FetchResponseMsg)),
|
||||
FromSWManager(SWManagerMsg),
|
||||
}
|
||||
// Get one incoming request.
|
||||
|
@ -1198,11 +1184,6 @@ where
|
|||
recv(self.layout_receiver) -> msg => {
|
||||
msg.expect("Unexpected layout channel panic in constellation").map(Request::Layout)
|
||||
}
|
||||
recv(self.network_listener_receiver) -> msg => {
|
||||
Ok(Request::NetworkListener(
|
||||
msg.expect("Unexpected network listener channel panic in constellation")
|
||||
))
|
||||
}
|
||||
recv(self.swmanager_receiver) -> msg => {
|
||||
msg.expect("Unexpected SW channel panic in constellation").map(Request::FromSWManager)
|
||||
}
|
||||
|
@ -1228,9 +1209,6 @@ where
|
|||
Request::Layout(message) => {
|
||||
self.handle_request_from_layout(message);
|
||||
},
|
||||
Request::NetworkListener(message) => {
|
||||
self.handle_request_from_network_listener(message);
|
||||
},
|
||||
Request::FromSWManager(message) => {
|
||||
self.handle_request_from_swmanager(message);
|
||||
},
|
||||
|
@ -1263,26 +1241,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
|
||||
)]
|
||||
fn handle_request_from_network_listener(&mut self, message: (PipelineId, FetchResponseMsg)) {
|
||||
let (id, message_) = message;
|
||||
let result = match self.pipelines.get(&id) {
|
||||
Some(pipeline) => {
|
||||
let msg = ConstellationControlMsg::NavigationResponse(id, message_);
|
||||
pipeline.event_loop.send(msg)
|
||||
},
|
||||
None => {
|
||||
return warn!("{}: Got fetch data after closure!", id);
|
||||
},
|
||||
};
|
||||
if let Err(e) = result {
|
||||
self.handle_send_error(id, e);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_request_from_swmanager(&mut self, message: SWManagerMsg) {
|
||||
match message {
|
||||
SWManagerMsg::PostMessageToClient => {
|
||||
|
@ -1615,10 +1573,6 @@ where
|
|||
FromScriptMsg::DiscardTopLevelBrowsingContext => {
|
||||
self.handle_close_top_level_browsing_context(source_top_ctx_id);
|
||||
},
|
||||
|
||||
FromScriptMsg::InitiateNavigateRequest(req_init, cancel_chan) => {
|
||||
self.handle_navigate_request(source_pipeline_id, req_init, cancel_chan);
|
||||
},
|
||||
FromScriptMsg::ScriptLoadedURLInIFrame(load_info) => {
|
||||
self.handle_script_loaded_url_in_iframe_msg(load_info);
|
||||
},
|
||||
|
@ -3202,26 +3156,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
|
||||
)]
|
||||
fn handle_navigate_request(
|
||||
&self,
|
||||
id: PipelineId,
|
||||
request_builder: RequestBuilder,
|
||||
cancel_chan: IpcReceiver<()>,
|
||||
) {
|
||||
let listener = NetworkListener::new(
|
||||
request_builder,
|
||||
id,
|
||||
self.public_resource_threads.clone(),
|
||||
self.network_listener_sender.clone(),
|
||||
);
|
||||
|
||||
listener.initiate_fetch(Some(cancel_chan));
|
||||
}
|
||||
|
||||
// The script thread associated with pipeline_id has loaded a URL in an
|
||||
// iframe via script. This will result in a new pipeline being spawned and
|
||||
// a child being added to the parent browsing context. This message is never
|
||||
|
|
|
@ -11,7 +11,6 @@ mod browsingcontext;
|
|||
mod constellation;
|
||||
mod event_loop;
|
||||
mod logging;
|
||||
mod network_listener;
|
||||
mod pipeline;
|
||||
mod sandboxing;
|
||||
mod serviceworker;
|
||||
|
|
|
@ -1,192 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! The listener that encapsulates all state for an in-progress document request.
|
||||
//! Any redirects that are encountered are followed. Whenever a non-redirect
|
||||
//! response is received, it is forwarded to the appropriate script thread.
|
||||
|
||||
use base::id::PipelineId;
|
||||
use crossbeam_channel::Sender;
|
||||
use http::{header, HeaderMap};
|
||||
use ipc_channel::ipc;
|
||||
use ipc_channel::router::ROUTER;
|
||||
use log::warn;
|
||||
use net::http_loader::{set_default_accept_language, DOCUMENT_ACCEPT_HEADER_VALUE};
|
||||
use net_traits::request::{Referrer, RequestBuilder, RequestId};
|
||||
use net_traits::response::ResponseInit;
|
||||
use net_traits::{
|
||||
CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseMsg, IpcSend, NetworkError,
|
||||
ResourceThreads,
|
||||
};
|
||||
|
||||
pub struct NetworkListener {
|
||||
res_init: Option<ResponseInit>,
|
||||
request_builder: RequestBuilder,
|
||||
pipeline_id: PipelineId,
|
||||
resource_threads: ResourceThreads,
|
||||
sender: Sender<(PipelineId, FetchResponseMsg)>,
|
||||
should_send: bool,
|
||||
}
|
||||
|
||||
impl NetworkListener {
|
||||
pub fn new(
|
||||
request_builder: RequestBuilder,
|
||||
pipeline_id: PipelineId,
|
||||
resource_threads: ResourceThreads,
|
||||
sender: Sender<(PipelineId, FetchResponseMsg)>,
|
||||
) -> NetworkListener {
|
||||
NetworkListener {
|
||||
res_init: None,
|
||||
request_builder,
|
||||
pipeline_id,
|
||||
resource_threads,
|
||||
sender,
|
||||
should_send: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initiate_fetch(&self, cancel_chan: Option<ipc::IpcReceiver<()>>) {
|
||||
let (ipc_sender, ipc_receiver) = ipc::channel().expect("Failed to create IPC channel!");
|
||||
|
||||
let mut listener = NetworkListener {
|
||||
res_init: self.res_init.clone(),
|
||||
request_builder: self.request_builder.clone(),
|
||||
resource_threads: self.resource_threads.clone(),
|
||||
sender: self.sender.clone(),
|
||||
pipeline_id: self.pipeline_id,
|
||||
should_send: false,
|
||||
};
|
||||
|
||||
let msg = match self.res_init {
|
||||
Some(ref res_init_) => CoreResourceMsg::FetchRedirect(
|
||||
self.request_builder.clone(),
|
||||
res_init_.clone(),
|
||||
ipc_sender,
|
||||
None,
|
||||
),
|
||||
None => {
|
||||
if !listener
|
||||
.request_builder
|
||||
.headers
|
||||
.contains_key(header::ACCEPT)
|
||||
{
|
||||
listener
|
||||
.request_builder
|
||||
.headers
|
||||
.insert(header::ACCEPT, DOCUMENT_ACCEPT_HEADER_VALUE);
|
||||
}
|
||||
|
||||
set_default_accept_language(&mut listener.request_builder.headers);
|
||||
|
||||
CoreResourceMsg::Fetch(
|
||||
listener.request_builder.clone(),
|
||||
FetchChannels::ResponseMsg(ipc_sender, cancel_chan),
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
ROUTER.add_typed_route(
|
||||
ipc_receiver,
|
||||
Box::new(move |message| {
|
||||
match message {
|
||||
Ok(FetchResponseMsg::ProcessResponse(request_id, res)) => {
|
||||
listener.check_redirect(request_id, res)
|
||||
},
|
||||
Ok(msg_) => listener.send(msg_),
|
||||
Err(e) => warn!("Error while receiving network listener message: {}", e),
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
if let Err(e) = self.resource_threads.sender().send(msg) {
|
||||
warn!("Resource thread unavailable ({})", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_redirect(
|
||||
&mut self,
|
||||
request_id: RequestId,
|
||||
message: Result<FetchMetadata, NetworkError>,
|
||||
) {
|
||||
match message {
|
||||
Ok(res_metadata) => {
|
||||
let metadata = match res_metadata {
|
||||
FetchMetadata::Filtered { ref unsafe_, .. } => unsafe_,
|
||||
FetchMetadata::Unfiltered(ref m) => m,
|
||||
};
|
||||
|
||||
match metadata.location_url {
|
||||
// https://html.spec.whatwg.org/multipage/#process-a-navigate-fetch
|
||||
// Step 7-4.
|
||||
Some(Ok(ref location_url))
|
||||
if matches!(location_url.scheme(), "http" | "https") =>
|
||||
{
|
||||
if self.request_builder.url_list.is_empty() {
|
||||
self.request_builder
|
||||
.url_list
|
||||
.push(self.request_builder.url.clone());
|
||||
}
|
||||
self.request_builder
|
||||
.url_list
|
||||
.push(metadata.final_url.clone());
|
||||
|
||||
self.request_builder.referrer = metadata
|
||||
.referrer
|
||||
.clone()
|
||||
.map(Referrer::ReferrerUrl)
|
||||
.unwrap_or(Referrer::NoReferrer);
|
||||
self.request_builder.referrer_policy = metadata.referrer_policy;
|
||||
|
||||
let headers = if let Some(ref headers) = metadata.headers {
|
||||
headers.clone().into_inner()
|
||||
} else {
|
||||
HeaderMap::new()
|
||||
};
|
||||
|
||||
self.res_init = Some(ResponseInit {
|
||||
url: metadata.final_url.clone(),
|
||||
location_url: metadata.location_url.clone(),
|
||||
headers,
|
||||
referrer: metadata.referrer.clone(),
|
||||
status_code: metadata
|
||||
.status
|
||||
.try_code()
|
||||
.map(|code| code.as_u16())
|
||||
.unwrap_or(200),
|
||||
});
|
||||
|
||||
// XXXManishearth we don't have the cancel_chan anymore and
|
||||
// can't use it here.
|
||||
//
|
||||
// Ideally the Fetch code would handle manual redirects on its own
|
||||
self.initiate_fetch(None);
|
||||
},
|
||||
_ => {
|
||||
// Response should be processed by script thread.
|
||||
self.should_send = true;
|
||||
self.send(FetchResponseMsg::ProcessResponse(
|
||||
request_id,
|
||||
Ok(res_metadata),
|
||||
));
|
||||
},
|
||||
};
|
||||
},
|
||||
Err(e) => {
|
||||
self.should_send = true;
|
||||
self.send(FetchResponseMsg::ProcessResponse(request_id, Err(e)))
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn send(&mut self, msg: FetchResponseMsg) {
|
||||
if self.should_send {
|
||||
if let Err(e) = self.sender.send((self.pipeline_id, msg)) {
|
||||
warn!(
|
||||
"Failed to forward network message to pipeline {:?}: {:?}",
|
||||
self.pipeline_id, e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -150,7 +150,6 @@ mod from_script {
|
|||
},
|
||||
Self::ScheduleBroadcast(..) => target!("ScheduleBroadcast"),
|
||||
Self::ForwardToEmbedder(msg) => msg.log_target(),
|
||||
Self::InitiateNavigateRequest(..) => target!("InitiateNavigateRequest"),
|
||||
Self::BroadcastStorageEvent(..) => target!("BroadcastStorageEvent"),
|
||||
Self::ChangeRunningAnimationsState(..) => target!("ChangeRunningAnimationsState"),
|
||||
Self::CreateCanvasPaintThread(..) => target!("CreateCanvasPaintThread"),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue