mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00: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"),
|
||||
|
|
|
@ -661,6 +661,7 @@ impl RemoteWebFontDownloader {
|
|||
&core_resource_thread_clone,
|
||||
request,
|
||||
None,
|
||||
None,
|
||||
Box::new(move |response_message| {
|
||||
match downloader.handle_web_font_fetch_message(response_message) {
|
||||
DownloaderResponseResult::InProcess => {},
|
||||
|
|
|
@ -28,8 +28,8 @@ use net_traits::request::{
|
|||
};
|
||||
use net_traits::response::{Response, ResponseBody, ResponseType};
|
||||
use net_traits::{
|
||||
FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
|
||||
ResourceTimeValue, ResourceTimingType,
|
||||
set_default_accept_language, FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceAttribute,
|
||||
ResourceFetchTiming, ResourceTimeValue, ResourceTimingType,
|
||||
};
|
||||
use rustls_pki_types::CertificateDer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -40,10 +40,7 @@ use tokio::sync::mpsc::{UnboundedReceiver as TokioReceiver, UnboundedSender as T
|
|||
use crate::fetch::cors_cache::CorsCache;
|
||||
use crate::fetch::headers::determine_nosniff;
|
||||
use crate::filemanager_thread::FileManager;
|
||||
use crate::http_loader::{
|
||||
determine_requests_referrer, http_fetch, set_default_accept, set_default_accept_language,
|
||||
HttpState,
|
||||
};
|
||||
use crate::http_loader::{determine_requests_referrer, http_fetch, set_default_accept, HttpState};
|
||||
use crate::protocols::ProtocolRegistry;
|
||||
use crate::subresource_integrity::is_response_integrity_valid;
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ use net_traits::response::{HttpsState, Response, ResponseBody, ResponseType};
|
|||
use net_traits::{
|
||||
CookieSource, FetchMetadata, NetworkError, RedirectEndValue, RedirectStartValue,
|
||||
ReferrerPolicy, ResourceAttribute, ResourceFetchTiming, ResourceTimeValue,
|
||||
DOCUMENT_ACCEPT_HEADER_VALUE,
|
||||
};
|
||||
use servo_arc::Arc;
|
||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||
|
@ -77,10 +78,6 @@ use crate::hsts::HstsList;
|
|||
use crate::http_cache::{CacheKey, HttpCache};
|
||||
use crate::resource_thread::{AuthCache, AuthCacheEntry};
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#document-accept-header-value>
|
||||
pub const DOCUMENT_ACCEPT_HEADER_VALUE: HeaderValue =
|
||||
HeaderValue::from_static("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||
|
||||
/// The various states an entry of the HttpCache can be in.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum HttpCacheEntryState {
|
||||
|
@ -146,18 +143,6 @@ fn set_default_accept_encoding(headers: &mut HeaderMap) {
|
|||
);
|
||||
}
|
||||
|
||||
pub fn set_default_accept_language(headers: &mut HeaderMap) {
|
||||
if headers.contains_key(header::ACCEPT_LANGUAGE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(eijebong): Change this once typed headers are done
|
||||
headers.insert(
|
||||
header::ACCEPT_LANGUAGE,
|
||||
HeaderValue::from_static("en-US,en;q=0.5"),
|
||||
);
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-state-no-referrer-when-downgrade>
|
||||
fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
|
||||
// Step 1
|
||||
|
|
|
@ -141,6 +141,7 @@ impl DocumentLoader {
|
|||
fetch_async(
|
||||
&self.resource_threads.core_thread,
|
||||
request,
|
||||
None, /* response_init */
|
||||
Some(canceller),
|
||||
callback,
|
||||
);
|
||||
|
|
|
@ -3262,6 +3262,7 @@ impl GlobalScope {
|
|||
fetch_async(
|
||||
&self.core_resource_thread(),
|
||||
request_builder,
|
||||
None,
|
||||
cancellation_receiver,
|
||||
network_listener.into_callback(),
|
||||
);
|
||||
|
|
|
@ -48,6 +48,7 @@ mod mem;
|
|||
#[allow(unsafe_code)]
|
||||
pub(crate) mod messaging;
|
||||
mod microtask;
|
||||
mod navigation;
|
||||
mod network_listener;
|
||||
#[allow(dead_code)]
|
||||
mod realms;
|
||||
|
|
|
@ -13,6 +13,7 @@ use crossbeam_channel::{select, Receiver, SendError, Sender};
|
|||
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg};
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use net_traits::image_cache::PendingImageResponse;
|
||||
use net_traits::FetchResponseMsg;
|
||||
use profile_traits::mem::{self as profile_mem, OpaqueSender, ReportsChan};
|
||||
use profile_traits::time::{self as profile_time};
|
||||
use script_traits::{ConstellationControlMsg, LayoutMsg, Painter, ScriptMsg};
|
||||
|
@ -49,7 +50,6 @@ impl MixedMessage {
|
|||
match self {
|
||||
MixedMessage::FromConstellation(ref inner_msg) => match *inner_msg {
|
||||
ConstellationControlMsg::StopDelayingLoadEventsMode(id) => Some(id),
|
||||
ConstellationControlMsg::NavigationResponse(id, _) => Some(id),
|
||||
ConstellationControlMsg::AttachLayout(ref new_layout_info) => new_layout_info
|
||||
.parent_info
|
||||
.or(Some(new_layout_info.new_pipeline_id)),
|
||||
|
@ -95,6 +95,7 @@ impl MixedMessage {
|
|||
pipeline_id
|
||||
},
|
||||
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(_)) => None,
|
||||
MainThreadScriptMsg::NavigationResponse { pipeline_id, .. } => Some(pipeline_id),
|
||||
MainThreadScriptMsg::WorkletLoaded(pipeline_id) => Some(pipeline_id),
|
||||
MainThreadScriptMsg::RegisterPaintWorklet { pipeline_id, .. } => Some(pipeline_id),
|
||||
MainThreadScriptMsg::Inactive => None,
|
||||
|
@ -116,6 +117,10 @@ pub(crate) enum MainThreadScriptMsg {
|
|||
/// Notifies the script thread that a new worklet has been loaded, and thus the page should be
|
||||
/// reflowed.
|
||||
WorkletLoaded(PipelineId),
|
||||
NavigationResponse {
|
||||
pipeline_id: PipelineId,
|
||||
message: Box<FetchResponseMsg>,
|
||||
},
|
||||
/// Notifies the script thread that a new paint worklet has been registered.
|
||||
RegisterPaintWorklet {
|
||||
pipeline_id: PipelineId,
|
||||
|
|
224
components/script/navigation.rs
Normal file
224
components/script/navigation.rs
Normal file
|
@ -0,0 +1,224 @@
|
|||
/* 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 std::cell::Cell;
|
||||
|
||||
use base::cross_process_instant::CrossProcessInstant;
|
||||
use base::id::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId};
|
||||
use content_security_policy::Destination;
|
||||
use crossbeam_channel::Sender;
|
||||
use http::header;
|
||||
use ipc_channel::ipc;
|
||||
use net_traits::request::{CredentialsMode, RedirectMode, RequestBuilder, RequestMode};
|
||||
use net_traits::response::ResponseInit;
|
||||
use net_traits::{
|
||||
fetch_async, set_default_accept_language, BoxedFetchCallback, CoreResourceThread,
|
||||
FetchResponseMsg, Metadata, DOCUMENT_ACCEPT_HEADER_VALUE,
|
||||
};
|
||||
use script_traits::{DocumentActivity, LoadData, WindowSizeData};
|
||||
use servo_url::{MutableOrigin, ServoUrl};
|
||||
|
||||
use crate::fetch::FetchCanceller;
|
||||
use crate::messaging::MainThreadScriptMsg;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NavigationListener {
|
||||
request_builder: RequestBuilder,
|
||||
main_thread_sender: Sender<MainThreadScriptMsg>,
|
||||
// Whether or not results are sent to the main thread. After a redirect results are no longer sent,
|
||||
// as the main thread has already started a new request.
|
||||
send_results_to_main_thread: Cell<bool>,
|
||||
}
|
||||
|
||||
impl NavigationListener {
|
||||
pub(crate) fn into_callback(self) -> BoxedFetchCallback {
|
||||
Box::new(move |response_msg| self.notify_fetch(response_msg))
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
request_builder: RequestBuilder,
|
||||
main_thread_sender: Sender<MainThreadScriptMsg>,
|
||||
) -> NavigationListener {
|
||||
NavigationListener {
|
||||
request_builder,
|
||||
main_thread_sender,
|
||||
send_results_to_main_thread: Cell::new(true),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initiate_fetch(
|
||||
self,
|
||||
core_resource_thread: &CoreResourceThread,
|
||||
response_init: Option<ResponseInit>,
|
||||
cancellation_receiver: Option<ipc::IpcReceiver<()>>,
|
||||
) {
|
||||
fetch_async(
|
||||
core_resource_thread,
|
||||
self.request_builder.clone(),
|
||||
response_init,
|
||||
cancellation_receiver,
|
||||
self.into_callback(),
|
||||
);
|
||||
}
|
||||
|
||||
fn notify_fetch(&self, message: FetchResponseMsg) {
|
||||
// If we've already asked the main thread to redirect the response, then stop sending results
|
||||
// for this fetch. The main thread has already replaced it.
|
||||
if !self.send_results_to_main_thread.get() {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is a redirect, don't send any more message after this one.
|
||||
if Self::http_redirect_metadata(&message).is_some() {
|
||||
self.send_results_to_main_thread.set(false);
|
||||
}
|
||||
|
||||
let pipeline_id = self
|
||||
.request_builder
|
||||
.pipeline_id
|
||||
.expect("Navigation should always have an associated Pipeline");
|
||||
let result = self
|
||||
.main_thread_sender
|
||||
.send(MainThreadScriptMsg::NavigationResponse {
|
||||
pipeline_id,
|
||||
message: Box::new(message),
|
||||
});
|
||||
|
||||
if let Err(error) = result {
|
||||
warn!(
|
||||
"Failed to send network message to pipeline {:?}: {error:?}",
|
||||
pipeline_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn http_redirect_metadata(message: &FetchResponseMsg) -> Option<&Metadata> {
|
||||
let FetchResponseMsg::ProcessResponse(_, Ok(ref metadata)) = message else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// Don't allow redirects for non HTTP(S) URLs.
|
||||
let metadata = metadata.metadata();
|
||||
if !matches!(
|
||||
metadata.location_url,
|
||||
Some(Ok(ref location_url)) if matches!(location_url.scheme(), "http" | "https")
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(metadata)
|
||||
}
|
||||
}
|
||||
|
||||
/// A document load that is in the process of fetching the requested resource. Contains
|
||||
/// data that will need to be present when the document and frame tree entry are created,
|
||||
/// but is only easily available at initiation of the load and on a push basis (so some
|
||||
/// data will be updated according to future resize events, viewport changes, etc.)
|
||||
#[derive(JSTraceable)]
|
||||
pub(crate) struct InProgressLoad {
|
||||
/// The pipeline which requested this load.
|
||||
#[no_trace]
|
||||
pub(crate) pipeline_id: PipelineId,
|
||||
/// The browsing context being loaded into.
|
||||
#[no_trace]
|
||||
pub(crate) browsing_context_id: BrowsingContextId,
|
||||
/// The top level ancestor browsing context.
|
||||
#[no_trace]
|
||||
pub(crate) top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
/// The parent pipeline and frame type associated with this load, if any.
|
||||
#[no_trace]
|
||||
pub(crate) parent_info: Option<PipelineId>,
|
||||
/// The opener, if this is an auxiliary.
|
||||
#[no_trace]
|
||||
pub(crate) opener: Option<BrowsingContextId>,
|
||||
/// The current window size associated with this pipeline.
|
||||
#[no_trace]
|
||||
pub(crate) window_size: WindowSizeData,
|
||||
/// The activity level of the document (inactive, active or fully active).
|
||||
#[no_trace]
|
||||
pub(crate) activity: DocumentActivity,
|
||||
/// Window is throttled, running timers at a heavily limited rate.
|
||||
pub(crate) throttled: bool,
|
||||
/// The origin for the document
|
||||
#[no_trace]
|
||||
pub(crate) origin: MutableOrigin,
|
||||
/// Timestamp reporting the time when the browser started this load.
|
||||
#[no_trace]
|
||||
pub(crate) navigation_start: CrossProcessInstant,
|
||||
/// For cancelling the fetch
|
||||
pub(crate) canceller: FetchCanceller,
|
||||
/// The [`LoadData`] associated with this load.
|
||||
#[no_trace]
|
||||
pub(crate) load_data: LoadData,
|
||||
/// A list of URL to keep track of all the redirects that have happened during
|
||||
/// this load.
|
||||
#[no_trace]
|
||||
pub(crate) url_list: Vec<ServoUrl>,
|
||||
}
|
||||
|
||||
impl InProgressLoad {
|
||||
/// Create a new InProgressLoad object.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
id: PipelineId,
|
||||
browsing_context_id: BrowsingContextId,
|
||||
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
parent_info: Option<PipelineId>,
|
||||
opener: Option<BrowsingContextId>,
|
||||
window_size: WindowSizeData,
|
||||
origin: MutableOrigin,
|
||||
load_data: LoadData,
|
||||
) -> InProgressLoad {
|
||||
let url = load_data.url.clone();
|
||||
InProgressLoad {
|
||||
pipeline_id: id,
|
||||
browsing_context_id,
|
||||
top_level_browsing_context_id,
|
||||
parent_info,
|
||||
opener,
|
||||
window_size,
|
||||
activity: DocumentActivity::FullyActive,
|
||||
throttled: false,
|
||||
origin,
|
||||
navigation_start: CrossProcessInstant::now(),
|
||||
canceller: Default::default(),
|
||||
load_data,
|
||||
url_list: vec![url],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn request_builder(&mut self) -> RequestBuilder {
|
||||
let id = self.pipeline_id;
|
||||
let top_level_browsing_context_id = self.top_level_browsing_context_id;
|
||||
let mut request_builder =
|
||||
RequestBuilder::new(self.load_data.url.clone(), self.load_data.referrer.clone())
|
||||
.method(self.load_data.method.clone())
|
||||
.destination(Destination::Document)
|
||||
.mode(RequestMode::Navigate)
|
||||
.credentials_mode(CredentialsMode::Include)
|
||||
.use_url_credentials(true)
|
||||
.pipeline_id(Some(id))
|
||||
.target_browsing_context_id(Some(top_level_browsing_context_id))
|
||||
.referrer_policy(self.load_data.referrer_policy)
|
||||
.headers(self.load_data.headers.clone())
|
||||
.body(self.load_data.data.clone())
|
||||
.redirect_mode(RedirectMode::Manual)
|
||||
.origin(self.origin.immutable().clone())
|
||||
.crash(self.load_data.crash.clone());
|
||||
request_builder.url_list = self.url_list.clone();
|
||||
|
||||
if !request_builder.headers.contains_key(header::ACCEPT) {
|
||||
request_builder
|
||||
.headers
|
||||
.insert(header::ACCEPT, DOCUMENT_ACCEPT_HEADER_VALUE);
|
||||
}
|
||||
set_default_accept_language(&mut request_builder.headers);
|
||||
|
||||
request_builder
|
||||
}
|
||||
}
|
|
@ -63,9 +63,8 @@ use media::WindowGLContext;
|
|||
use metrics::{PaintTimeMetrics, MAX_TASK_NS};
|
||||
use mime::{self, Mime};
|
||||
use net_traits::image_cache::{ImageCache, PendingImageResponse};
|
||||
use net_traits::request::{
|
||||
CredentialsMode, Destination, RedirectMode, RequestBuilder, RequestId, RequestMode,
|
||||
};
|
||||
use net_traits::request::{Referrer, RequestId};
|
||||
use net_traits::response::ResponseInit;
|
||||
use net_traits::storage_thread::StorageType;
|
||||
use net_traits::{
|
||||
FetchMetadata, FetchResponseListener, FetchResponseMsg, Metadata, NetworkError,
|
||||
|
@ -140,12 +139,12 @@ use crate::dom::window::Window;
|
|||
use crate::dom::windowproxy::{CreatorBrowsingContextInfo, WindowProxy};
|
||||
use crate::dom::worklet::WorkletThreadPool;
|
||||
use crate::dom::workletglobalscope::WorkletGlobalScopeInit;
|
||||
use crate::fetch::FetchCanceller;
|
||||
use crate::messaging::{
|
||||
CommonScriptMsg, MainThreadScriptMsg, MixedMessage, ScriptEventLoopSender,
|
||||
ScriptThreadReceivers, ScriptThreadSenders,
|
||||
};
|
||||
use crate::microtask::{Microtask, MicrotaskQueue};
|
||||
use crate::navigation::{InProgressLoad, NavigationListener};
|
||||
use crate::realms::enter_realm;
|
||||
use crate::script_module::ScriptFetchOptions;
|
||||
use crate::script_runtime::{
|
||||
|
@ -181,83 +180,6 @@ pub(crate) unsafe fn trace_thread(tr: *mut JSTracer) {
|
|||
})
|
||||
}
|
||||
|
||||
/// A document load that is in the process of fetching the requested resource. Contains
|
||||
/// data that will need to be present when the document and frame tree entry are created,
|
||||
/// but is only easily available at initiation of the load and on a push basis (so some
|
||||
/// data will be updated according to future resize events, viewport changes, etc.)
|
||||
#[derive(JSTraceable)]
|
||||
struct InProgressLoad {
|
||||
/// The pipeline which requested this load.
|
||||
#[no_trace]
|
||||
pipeline_id: PipelineId,
|
||||
/// The browsing context being loaded into.
|
||||
#[no_trace]
|
||||
browsing_context_id: BrowsingContextId,
|
||||
/// The top level ancestor browsing context.
|
||||
#[no_trace]
|
||||
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
/// The parent pipeline and frame type associated with this load, if any.
|
||||
#[no_trace]
|
||||
parent_info: Option<PipelineId>,
|
||||
/// The opener, if this is an auxiliary.
|
||||
#[no_trace]
|
||||
opener: Option<BrowsingContextId>,
|
||||
/// The current window size associated with this pipeline.
|
||||
#[no_trace]
|
||||
window_size: WindowSizeData,
|
||||
/// The activity level of the document (inactive, active or fully active).
|
||||
#[no_trace]
|
||||
activity: DocumentActivity,
|
||||
/// Window is throttled, running timers at a heavily limited rate.
|
||||
throttled: bool,
|
||||
/// The requested URL of the load.
|
||||
#[no_trace]
|
||||
url: ServoUrl,
|
||||
/// The origin for the document
|
||||
#[no_trace]
|
||||
origin: MutableOrigin,
|
||||
/// Timestamp reporting the time when the browser started this load.
|
||||
#[no_trace]
|
||||
navigation_start: CrossProcessInstant,
|
||||
/// For cancelling the fetch
|
||||
canceller: FetchCanceller,
|
||||
/// If inheriting the security context
|
||||
inherited_secure_context: Option<bool>,
|
||||
}
|
||||
|
||||
impl InProgressLoad {
|
||||
/// Create a new InProgressLoad object.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn new(
|
||||
id: PipelineId,
|
||||
browsing_context_id: BrowsingContextId,
|
||||
top_level_browsing_context_id: TopLevelBrowsingContextId,
|
||||
parent_info: Option<PipelineId>,
|
||||
opener: Option<BrowsingContextId>,
|
||||
window_size: WindowSizeData,
|
||||
url: ServoUrl,
|
||||
origin: MutableOrigin,
|
||||
inherited_secure_context: Option<bool>,
|
||||
) -> InProgressLoad {
|
||||
let navigation_start = CrossProcessInstant::now();
|
||||
InProgressLoad {
|
||||
pipeline_id: id,
|
||||
browsing_context_id,
|
||||
top_level_browsing_context_id,
|
||||
parent_info,
|
||||
opener,
|
||||
window_size,
|
||||
activity: DocumentActivity::FullyActive,
|
||||
throttled: false,
|
||||
url,
|
||||
origin,
|
||||
navigation_start,
|
||||
canceller: Default::default(),
|
||||
inherited_secure_context,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We borrow the incomplete parser contexts mutably during parsing,
|
||||
// which is fine except that parsing can trigger evaluation,
|
||||
// which can trigger GC, and so we can end up tracing the script
|
||||
|
@ -493,7 +415,6 @@ impl ScriptThreadFactory for ScriptThread {
|
|||
let top_level_browsing_context_id = state.top_level_browsing_context_id;
|
||||
let parent_info = state.parent_info;
|
||||
let opener = state.opener;
|
||||
let secure = load_data.inherited_secure_context;
|
||||
let memory_profiler_sender = state.memory_profiler_sender.clone();
|
||||
let window_size = state.window_size;
|
||||
|
||||
|
@ -507,18 +428,16 @@ impl ScriptThreadFactory for ScriptThread {
|
|||
let mut failsafe = ScriptMemoryFailsafe::new(&script_thread);
|
||||
|
||||
let origin = MutableOrigin::new(load_data.url.origin());
|
||||
let new_load = InProgressLoad::new(
|
||||
script_thread.pre_page_load(InProgressLoad::new(
|
||||
id,
|
||||
browsing_context_id,
|
||||
top_level_browsing_context_id,
|
||||
parent_info,
|
||||
opener,
|
||||
window_size,
|
||||
load_data.url.clone(),
|
||||
origin,
|
||||
secure,
|
||||
);
|
||||
script_thread.pre_page_load(new_load, load_data);
|
||||
load_data,
|
||||
));
|
||||
|
||||
let reporter_name = format!("script-reporter-{:?}", id);
|
||||
memory_profiler_sender.run_with_memory_reporting(
|
||||
|
@ -1822,20 +1741,6 @@ impl ScriptThread {
|
|||
ConstellationControlMsg::StopDelayingLoadEventsMode(pipeline_id) => {
|
||||
self.handle_stop_delaying_load_events_mode(pipeline_id)
|
||||
},
|
||||
ConstellationControlMsg::NavigationResponse(pipeline_id, fetch_data) => {
|
||||
match fetch_data {
|
||||
FetchResponseMsg::ProcessResponse(request_id, metadata) => {
|
||||
self.handle_fetch_metadata(pipeline_id, request_id, metadata)
|
||||
},
|
||||
FetchResponseMsg::ProcessResponseChunk(request_id, chunk) => {
|
||||
self.handle_fetch_chunk(pipeline_id, request_id, chunk)
|
||||
},
|
||||
FetchResponseMsg::ProcessResponseEOF(request_id, eof) => {
|
||||
self.handle_fetch_eof(pipeline_id, request_id, eof)
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
},
|
||||
ConstellationControlMsg::NavigateIframe(
|
||||
parent_pipeline_id,
|
||||
browsing_context_id,
|
||||
|
@ -2079,6 +1984,12 @@ impl ScriptThread {
|
|||
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(chan)) => {
|
||||
self.collect_reports(chan)
|
||||
},
|
||||
MainThreadScriptMsg::NavigationResponse {
|
||||
pipeline_id,
|
||||
message,
|
||||
} => {
|
||||
self.handle_navigation_response(pipeline_id, *message);
|
||||
},
|
||||
MainThreadScriptMsg::WorkletLoaded(pipeline_id) => {
|
||||
self.handle_worklet_loaded(pipeline_id)
|
||||
},
|
||||
|
@ -2484,6 +2395,7 @@ impl ScriptThread {
|
|||
} = new_layout_info;
|
||||
|
||||
// Kick off the fetch for the new resource.
|
||||
let url = load_data.url.clone();
|
||||
let new_load = InProgressLoad::new(
|
||||
new_pipeline_id,
|
||||
browsing_context_id,
|
||||
|
@ -2491,16 +2403,15 @@ impl ScriptThread {
|
|||
parent_info,
|
||||
opener,
|
||||
window_size,
|
||||
load_data.url.clone(),
|
||||
origin,
|
||||
load_data.inherited_secure_context,
|
||||
load_data,
|
||||
);
|
||||
if load_data.url.as_str() == "about:blank" {
|
||||
self.start_page_load_about_blank(new_load, load_data);
|
||||
} else if load_data.url.as_str() == "about:srcdoc" {
|
||||
self.page_load_about_srcdoc(new_load, load_data);
|
||||
if url.as_str() == "about:blank" {
|
||||
self.start_page_load_about_blank(new_load);
|
||||
} else if url.as_str() == "about:srcdoc" {
|
||||
self.page_load_about_srcdoc(new_load);
|
||||
} else {
|
||||
self.pre_page_load(new_load, load_data);
|
||||
self.pre_page_load(new_load);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3118,7 +3029,7 @@ impl ScriptThread {
|
|||
}
|
||||
debug!(
|
||||
"ScriptThread: loading {} on pipeline {:?}",
|
||||
incomplete.url, incomplete.pipeline_id
|
||||
incomplete.load_data.url, incomplete.pipeline_id
|
||||
);
|
||||
|
||||
let origin = if final_url.as_str() == "about:blank" || final_url.as_str() == "about:srcdoc"
|
||||
|
@ -3201,7 +3112,7 @@ impl ScriptThread {
|
|||
self.player_context.clone(),
|
||||
#[cfg(feature = "webgpu")]
|
||||
self.gpu_id_hub.clone(),
|
||||
incomplete.inherited_secure_context,
|
||||
incomplete.load_data.inherited_secure_context,
|
||||
);
|
||||
|
||||
let _realm = enter_realm(&*window);
|
||||
|
@ -3508,42 +3419,48 @@ impl ScriptThread {
|
|||
|
||||
/// Instructs the constellation to fetch the document that will be loaded. Stores the InProgressLoad
|
||||
/// argument until a notification is received that the fetch is complete.
|
||||
fn pre_page_load(&self, mut incomplete: InProgressLoad, load_data: LoadData) {
|
||||
let id = incomplete.pipeline_id;
|
||||
let top_level_browsing_context_id = incomplete.top_level_browsing_context_id;
|
||||
let req_init = RequestBuilder::new(load_data.url.clone(), load_data.referrer)
|
||||
.method(load_data.method)
|
||||
.destination(Destination::Document)
|
||||
.mode(RequestMode::Navigate)
|
||||
.credentials_mode(CredentialsMode::Include)
|
||||
.use_url_credentials(true)
|
||||
.pipeline_id(Some(id))
|
||||
.target_browsing_context_id(Some(top_level_browsing_context_id))
|
||||
.referrer_policy(load_data.referrer_policy)
|
||||
.headers(load_data.headers)
|
||||
.body(load_data.data)
|
||||
.redirect_mode(RedirectMode::Manual)
|
||||
.origin(incomplete.origin.immutable().clone())
|
||||
.crash(load_data.crash);
|
||||
|
||||
let context = ParserContext::new(id, load_data.url);
|
||||
fn pre_page_load(&self, mut incomplete: InProgressLoad) {
|
||||
let context = ParserContext::new(incomplete.pipeline_id, incomplete.load_data.url.clone());
|
||||
self.incomplete_parser_contexts
|
||||
.0
|
||||
.borrow_mut()
|
||||
.push((id, context));
|
||||
.push((incomplete.pipeline_id, context));
|
||||
|
||||
let cancel_chan = incomplete.canceller.initialize();
|
||||
let cancellation_receiver = incomplete.canceller.initialize();
|
||||
NavigationListener::new(
|
||||
incomplete.request_builder(),
|
||||
self.senders.self_sender.clone(),
|
||||
)
|
||||
.initiate_fetch(
|
||||
&self.resource_threads.core_thread,
|
||||
None,
|
||||
Some(cancellation_receiver),
|
||||
);
|
||||
|
||||
self.senders
|
||||
.pipeline_to_constellation_sender
|
||||
.send((
|
||||
id,
|
||||
ScriptMsg::InitiateNavigateRequest(req_init, cancel_chan),
|
||||
))
|
||||
.unwrap();
|
||||
self.incomplete_loads.borrow_mut().push(incomplete);
|
||||
}
|
||||
|
||||
fn handle_navigation_response(&self, pipeline_id: PipelineId, message: FetchResponseMsg) {
|
||||
if let Some(metadata) = NavigationListener::http_redirect_metadata(&message) {
|
||||
self.handle_navigation_redirect(pipeline_id, metadata);
|
||||
return;
|
||||
};
|
||||
|
||||
match message {
|
||||
FetchResponseMsg::ProcessResponse(request_id, metadata) => {
|
||||
self.handle_fetch_metadata(pipeline_id, request_id, metadata)
|
||||
},
|
||||
FetchResponseMsg::ProcessResponseChunk(request_id, chunk) => {
|
||||
self.handle_fetch_chunk(pipeline_id, request_id, chunk)
|
||||
},
|
||||
FetchResponseMsg::ProcessResponseEOF(request_id, eof) => {
|
||||
self.handle_fetch_eof(pipeline_id, request_id, eof)
|
||||
},
|
||||
FetchResponseMsg::ProcessRequestBody(..) => {},
|
||||
FetchResponseMsg::ProcessRequestEOF(..) => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_fetch_metadata(
|
||||
&self,
|
||||
id: PipelineId,
|
||||
|
@ -3596,24 +3513,74 @@ impl ScriptThread {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_navigation_redirect(&self, id: PipelineId, metadata: &Metadata) {
|
||||
// TODO(mrobinson): This tries to accomplish some steps from
|
||||
// <https://html.spec.whatwg.org/multipage/#process-a-navigate-fetch>, but it's
|
||||
// very out of sync with the specification.
|
||||
assert!(metadata.location_url.is_some());
|
||||
|
||||
let mut incomplete_loads = self.incomplete_loads.borrow_mut();
|
||||
let Some(incomplete_load) = incomplete_loads
|
||||
.iter_mut()
|
||||
.find(|incomplete_load| incomplete_load.pipeline_id == id)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Update the `url_list` of the incomplete load to track all redirects. This will be reflected
|
||||
// in the new `RequestBuilder` as well.
|
||||
incomplete_load.url_list.push(metadata.final_url.clone());
|
||||
|
||||
let mut request_builder = incomplete_load.request_builder();
|
||||
request_builder.referrer = metadata
|
||||
.referrer
|
||||
.clone()
|
||||
.map(Referrer::ReferrerUrl)
|
||||
.unwrap_or(Referrer::NoReferrer);
|
||||
request_builder.referrer_policy = metadata.referrer_policy;
|
||||
|
||||
let headers = metadata
|
||||
.headers
|
||||
.as_ref()
|
||||
.map(|headers| headers.clone().into_inner())
|
||||
.unwrap_or_default();
|
||||
|
||||
let response_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),
|
||||
});
|
||||
|
||||
let cancellation_receiver = incomplete_load.canceller.initialize();
|
||||
NavigationListener::new(request_builder, self.senders.self_sender.clone()).initiate_fetch(
|
||||
&self.resource_threads.core_thread,
|
||||
response_init,
|
||||
Some(cancellation_receiver),
|
||||
);
|
||||
}
|
||||
|
||||
/// Synchronously fetch `about:blank`. Stores the `InProgressLoad`
|
||||
/// argument until a notification is received that the fetch is complete.
|
||||
fn start_page_load_about_blank(&self, incomplete: InProgressLoad, load_data: LoadData) {
|
||||
fn start_page_load_about_blank(&self, mut incomplete: InProgressLoad) {
|
||||
let id = incomplete.pipeline_id;
|
||||
|
||||
self.incomplete_loads.borrow_mut().push(incomplete);
|
||||
|
||||
let url = ServoUrl::parse("about:blank").unwrap();
|
||||
let mut context = ParserContext::new(id, url.clone());
|
||||
|
||||
let mut meta = Metadata::default(url);
|
||||
meta.set_content_type(Some(&mime::TEXT_HTML));
|
||||
meta.set_referrer_policy(load_data.referrer_policy);
|
||||
meta.set_referrer_policy(incomplete.load_data.referrer_policy);
|
||||
|
||||
// If this page load is the result of a javascript scheme url, map
|
||||
// the evaluation result into a response.
|
||||
let chunk = match load_data.js_eval_result {
|
||||
Some(JsEvalResult::Ok(content)) => content,
|
||||
let chunk = match incomplete.load_data.js_eval_result {
|
||||
Some(JsEvalResult::Ok(ref mut content)) => std::mem::take(content),
|
||||
Some(JsEvalResult::NoContent) => {
|
||||
meta.status = http::StatusCode::NO_CONTENT.into();
|
||||
vec![]
|
||||
|
@ -3621,6 +3588,8 @@ impl ScriptThread {
|
|||
None => vec![],
|
||||
};
|
||||
|
||||
self.incomplete_loads.borrow_mut().push(incomplete);
|
||||
|
||||
let dummy_request_id = RequestId::next();
|
||||
context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta)));
|
||||
context.process_response_chunk(dummy_request_id, chunk);
|
||||
|
@ -3631,20 +3600,20 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
/// Synchronously parse a srcdoc document from a giving HTML string.
|
||||
fn page_load_about_srcdoc(&self, incomplete: InProgressLoad, load_data: LoadData) {
|
||||
fn page_load_about_srcdoc(&self, mut incomplete: InProgressLoad) {
|
||||
let id = incomplete.pipeline_id;
|
||||
|
||||
let url = ServoUrl::parse("about:srcdoc").unwrap();
|
||||
let mut meta = Metadata::default(url.clone());
|
||||
meta.set_content_type(Some(&mime::TEXT_HTML));
|
||||
meta.set_referrer_policy(incomplete.load_data.referrer_policy);
|
||||
|
||||
let srcdoc = std::mem::take(&mut incomplete.load_data.srcdoc);
|
||||
let chunk = srcdoc.into_bytes();
|
||||
|
||||
self.incomplete_loads.borrow_mut().push(incomplete);
|
||||
|
||||
let url = ServoUrl::parse("about:srcdoc").unwrap();
|
||||
let mut context = ParserContext::new(id, url.clone());
|
||||
|
||||
let mut meta = Metadata::default(url);
|
||||
meta.set_content_type(Some(&mime::TEXT_HTML));
|
||||
meta.set_referrer_policy(load_data.referrer_policy);
|
||||
|
||||
let chunk = load_data.srcdoc.into_bytes();
|
||||
|
||||
let mut context = ParserContext::new(id, url);
|
||||
let dummy_request_id = RequestId::next();
|
||||
context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta)));
|
||||
context.process_response_chunk(dummy_request_id, chunk);
|
||||
|
|
|
@ -14,7 +14,7 @@ use base::id::HistoryStateId;
|
|||
use cookie::Cookie;
|
||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||
use headers::{ContentType, HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
|
||||
use http::{Error as HttpError, HeaderMap, StatusCode};
|
||||
use http::{header, Error as HttpError, HeaderMap, HeaderValue, StatusCode};
|
||||
use hyper_serde::Serde;
|
||||
use hyper_util::client::legacy::Error as HyperError;
|
||||
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
||||
|
@ -46,6 +46,10 @@ pub mod request;
|
|||
pub mod response;
|
||||
pub mod storage_thread;
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#document-accept-header-value>
|
||||
pub const DOCUMENT_ACCEPT_HEADER_VALUE: HeaderValue =
|
||||
HeaderValue::from_static("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||
|
||||
/// An implementation of the [Fetch specification](https://fetch.spec.whatwg.org/)
|
||||
pub mod fetch {
|
||||
pub mod headers;
|
||||
|
@ -197,7 +201,7 @@ pub enum FetchResponseMsg {
|
|||
}
|
||||
|
||||
impl FetchResponseMsg {
|
||||
fn request_id(&self) -> RequestId {
|
||||
pub fn request_id(&self) -> RequestId {
|
||||
match self {
|
||||
FetchResponseMsg::ProcessRequestBody(id) |
|
||||
FetchResponseMsg::ProcessRequestEOF(id) |
|
||||
|
@ -252,6 +256,15 @@ pub enum FetchMetadata {
|
|||
},
|
||||
}
|
||||
|
||||
impl FetchMetadata {
|
||||
pub fn metadata(&self) -> &Metadata {
|
||||
match self {
|
||||
Self::Unfiltered(metadata) => metadata,
|
||||
Self::Filtered { unsafe_, .. } => unsafe_,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FetchResponseListener {
|
||||
fn process_request_body(&mut self, request_id: RequestId);
|
||||
fn process_request_eof(&mut self, request_id: RequestId);
|
||||
|
@ -511,6 +524,7 @@ pub enum CoreResourceMsg {
|
|||
enum ToFetchThreadMessage {
|
||||
StartFetch(
|
||||
/* request_builder */ RequestBuilder,
|
||||
/* response_init */ Option<ResponseInit>,
|
||||
/* cancel_chan */ Option<IpcReceiver<()>>,
|
||||
/* callback */ BoxedFetchCallback,
|
||||
),
|
||||
|
@ -570,14 +584,29 @@ impl FetchThread {
|
|||
fn run(&mut self) {
|
||||
loop {
|
||||
match self.receiver.recv().unwrap() {
|
||||
ToFetchThreadMessage::StartFetch(request_builder, canceller, callback) => {
|
||||
ToFetchThreadMessage::StartFetch(
|
||||
request_builder,
|
||||
response_init,
|
||||
canceller,
|
||||
callback,
|
||||
) => {
|
||||
self.active_fetches.insert(request_builder.id, callback);
|
||||
self.core_resource_thread
|
||||
.send(CoreResourceMsg::Fetch(
|
||||
|
||||
// Only redirects have a `response_init` field.
|
||||
let message = match response_init {
|
||||
Some(response_init) => CoreResourceMsg::FetchRedirect(
|
||||
request_builder,
|
||||
response_init,
|
||||
self.to_fetch_sender.clone(),
|
||||
canceller,
|
||||
),
|
||||
None => CoreResourceMsg::Fetch(
|
||||
request_builder,
|
||||
FetchChannels::ResponseMsg(self.to_fetch_sender.clone(), canceller),
|
||||
))
|
||||
.unwrap();
|
||||
),
|
||||
};
|
||||
|
||||
self.core_resource_thread.send(message).unwrap();
|
||||
},
|
||||
ToFetchThreadMessage::FetchResponse(fetch_response_msg) => {
|
||||
let request_id = fetch_response_msg.request_id();
|
||||
|
@ -599,18 +628,23 @@ impl FetchThread {
|
|||
}
|
||||
}
|
||||
|
||||
/// Instruct the resource thread to make a new request.
|
||||
static FETCH_THREAD: OnceLock<Sender<ToFetchThreadMessage>> = OnceLock::new();
|
||||
|
||||
/// Instruct the resource thread to make a new fetch request.
|
||||
pub fn fetch_async(
|
||||
core_resource_thread: &CoreResourceThread,
|
||||
request: RequestBuilder,
|
||||
response_init: Option<ResponseInit>,
|
||||
canceller: Option<IpcReceiver<()>>,
|
||||
callback: BoxedFetchCallback,
|
||||
) {
|
||||
static FETCH_THREAD: OnceLock<Sender<ToFetchThreadMessage>> = OnceLock::new();
|
||||
let _ = FETCH_THREAD
|
||||
.get_or_init(|| FetchThread::spawn(core_resource_thread))
|
||||
.send(ToFetchThreadMessage::StartFetch(
|
||||
request, canceller, callback,
|
||||
request,
|
||||
response_init,
|
||||
canceller,
|
||||
callback,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -945,5 +979,17 @@ pub fn http_percent_encode(bytes: &[u8]) -> String {
|
|||
percent_encoding::percent_encode(bytes, HTTP_VALUE).to_string()
|
||||
}
|
||||
|
||||
pub fn set_default_accept_language(headers: &mut HeaderMap) {
|
||||
if headers.contains_key(header::ACCEPT_LANGUAGE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(eijebong): Change this once typed headers are done
|
||||
headers.insert(
|
||||
header::ACCEPT_LANGUAGE,
|
||||
HeaderValue::from_static("en-US,en;q=0.5"),
|
||||
);
|
||||
}
|
||||
|
||||
pub static PRIVILEGED_SECRET: LazyLock<u32> =
|
||||
LazyLock::new(|| servo_rand::ServoRng::default().next_u32());
|
||||
|
|
|
@ -47,7 +47,7 @@ use media::WindowGLContext;
|
|||
use net_traits::image_cache::ImageCache;
|
||||
use net_traits::request::{Referrer, RequestBody};
|
||||
use net_traits::storage_thread::StorageType;
|
||||
use net_traits::{FetchResponseMsg, ReferrerPolicy, ResourceThreads};
|
||||
use net_traits::{ReferrerPolicy, ResourceThreads};
|
||||
use pixels::{Image, PixelFormat};
|
||||
use profile_traits::{mem, time as profile_time};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
@ -286,16 +286,12 @@ pub enum UpdatePipelineIdReason {
|
|||
|
||||
/// Messages sent from the constellation or layout to the script thread.
|
||||
// FIXME: https://github.com/servo/servo/issues/34591
|
||||
#[expect(clippy::large_enum_variant)]
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub enum ConstellationControlMsg {
|
||||
/// Takes the associated window proxy out of "delaying-load-events-mode",
|
||||
/// used if a scheduled navigated was refused by the embedder.
|
||||
/// <https://html.spec.whatwg.org/multipage/#delaying-load-events-mode>
|
||||
StopDelayingLoadEventsMode(PipelineId),
|
||||
/// Sends the final response to script thread for fetching after all redirections
|
||||
/// have been resolved
|
||||
NavigationResponse(PipelineId, FetchResponseMsg),
|
||||
/// Gives a channel and ID to a layout, as well as the ID of that layout's parent
|
||||
AttachLayout(NewLayoutInfo),
|
||||
/// Window resized. Sends a DOM event eventually, but first we combine events.
|
||||
|
@ -413,7 +409,6 @@ impl fmt::Debug for ConstellationControlMsg {
|
|||
use self::ConstellationControlMsg::*;
|
||||
let variant = match *self {
|
||||
StopDelayingLoadEventsMode(..) => "StopDelayingLoadsEventMode",
|
||||
NavigationResponse(..) => "NavigationResponse",
|
||||
AttachLayout(..) => "AttachLayout",
|
||||
Resize(..) => "Resize",
|
||||
ThemeChange(..) => "ThemeChange",
|
||||
|
|
|
@ -17,7 +17,6 @@ use embedder_traits::{EmbedderMsg, MediaSessionEvent};
|
|||
use euclid::default::Size2D as UntypedSize2D;
|
||||
use euclid::Size2D;
|
||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||
use net_traits::request::RequestBuilder;
|
||||
use net_traits::storage_thread::StorageType;
|
||||
use net_traits::CoreResourceMsg;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -128,9 +127,6 @@ pub enum ScriptMsg {
|
|||
ScheduleBroadcast(BroadcastChannelRouterId, BroadcastMsg),
|
||||
/// Forward a message to the embedder.
|
||||
ForwardToEmbedder(EmbedderMsg),
|
||||
/// Requests are sent to constellation and fetches are checked manually
|
||||
/// for cross-origin loads
|
||||
InitiateNavigateRequest(RequestBuilder, /* cancellation_chan */ IpcReceiver<()>),
|
||||
/// Broadcast a storage event to every same-origin pipeline.
|
||||
/// The strings are key, old value and new value.
|
||||
BroadcastStorageEvent(
|
||||
|
@ -271,7 +267,6 @@ impl fmt::Debug for ScriptMsg {
|
|||
NewBroadcastChannelNameInRouter(..) => "NewBroadcastChannelNameInRouter",
|
||||
ScheduleBroadcast(..) => "ScheduleBroadcast",
|
||||
ForwardToEmbedder(..) => "ForwardToEmbedder",
|
||||
InitiateNavigateRequest(..) => "InitiateNavigateRequest",
|
||||
BroadcastStorageEvent(..) => "BroadcastStorageEvent",
|
||||
ChangeRunningAnimationsState(..) => "ChangeRunningAnimationsState",
|
||||
CreateCanvasPaintThread(..) => "CreateCanvasPaintThread",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue