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 log::{debug, error, info, trace, warn};
|
||||||
use media::{GLPlayerThreads, WindowGLContext};
|
use media::{GLPlayerThreads, WindowGLContext};
|
||||||
use net_traits::pub_domains::reg_host;
|
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::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 profile_traits::{mem, time};
|
||||||
use script_layout_interface::{LayoutFactory, ScriptThreadFactory};
|
use script_layout_interface::{LayoutFactory, ScriptThreadFactory};
|
||||||
use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent};
|
use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent};
|
||||||
|
@ -166,7 +166,6 @@ use crate::browsingcontext::{
|
||||||
NewBrowsingContextInfo,
|
NewBrowsingContextInfo,
|
||||||
};
|
};
|
||||||
use crate::event_loop::EventLoop;
|
use crate::event_loop::EventLoop;
|
||||||
use crate::network_listener::NetworkListener;
|
|
||||||
use crate::pipeline::{InitialPipelineState, Pipeline};
|
use crate::pipeline::{InitialPipelineState, Pipeline};
|
||||||
use crate::serviceworker::ServiceWorkerUnprivilegedContent;
|
use crate::serviceworker::ServiceWorkerUnprivilegedContent;
|
||||||
use crate::session_history::{
|
use crate::session_history::{
|
||||||
|
@ -317,12 +316,6 @@ pub struct Constellation<STF, SWF> {
|
||||||
/// This is the constellation's view of `layout_sender`.
|
/// This is the constellation's view of `layout_sender`.
|
||||||
layout_receiver: Receiver<Result<FromLayoutMsg, IpcError>>,
|
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.
|
/// A channel for the constellation to receive messages from the compositor thread.
|
||||||
compositor_receiver: Receiver<FromCompositorMsg>,
|
compositor_receiver: Receiver<FromCompositorMsg>,
|
||||||
|
|
||||||
|
@ -685,8 +678,6 @@ where
|
||||||
layout_ipc_receiver,
|
layout_ipc_receiver,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (network_listener_sender, network_listener_receiver) = unbounded();
|
|
||||||
|
|
||||||
let swmanager_receiver =
|
let swmanager_receiver =
|
||||||
route_ipc_receiver_to_new_crossbeam_receiver_preserving_errors(
|
route_ipc_receiver_to_new_crossbeam_receiver_preserving_errors(
|
||||||
swmanager_ipc_receiver,
|
swmanager_ipc_receiver,
|
||||||
|
@ -715,8 +706,6 @@ where
|
||||||
compositor_receiver,
|
compositor_receiver,
|
||||||
layout_factory,
|
layout_factory,
|
||||||
layout_receiver,
|
layout_receiver,
|
||||||
network_listener_sender,
|
|
||||||
network_listener_receiver,
|
|
||||||
embedder_proxy: state.embedder_proxy,
|
embedder_proxy: state.embedder_proxy,
|
||||||
compositor_proxy: state.compositor_proxy,
|
compositor_proxy: state.compositor_proxy,
|
||||||
webviews: WebViewManager::default(),
|
webviews: WebViewManager::default(),
|
||||||
|
@ -1156,15 +1145,12 @@ where
|
||||||
)]
|
)]
|
||||||
fn handle_request(&mut self) {
|
fn handle_request(&mut self) {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
// FIXME: https://github.com/servo/servo/issues/34591
|
|
||||||
#[expect(clippy::large_enum_variant)]
|
|
||||||
enum Request {
|
enum Request {
|
||||||
PipelineNamespace(PipelineNamespaceRequest),
|
PipelineNamespace(PipelineNamespaceRequest),
|
||||||
Script((PipelineId, FromScriptMsg)),
|
Script((PipelineId, FromScriptMsg)),
|
||||||
BackgroundHangMonitor(HangMonitorAlert),
|
BackgroundHangMonitor(HangMonitorAlert),
|
||||||
Compositor(FromCompositorMsg),
|
Compositor(FromCompositorMsg),
|
||||||
Layout(FromLayoutMsg),
|
Layout(FromLayoutMsg),
|
||||||
NetworkListener((PipelineId, FetchResponseMsg)),
|
|
||||||
FromSWManager(SWManagerMsg),
|
FromSWManager(SWManagerMsg),
|
||||||
}
|
}
|
||||||
// Get one incoming request.
|
// Get one incoming request.
|
||||||
|
@ -1198,11 +1184,6 @@ where
|
||||||
recv(self.layout_receiver) -> msg => {
|
recv(self.layout_receiver) -> msg => {
|
||||||
msg.expect("Unexpected layout channel panic in constellation").map(Request::Layout)
|
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 => {
|
recv(self.swmanager_receiver) -> msg => {
|
||||||
msg.expect("Unexpected SW channel panic in constellation").map(Request::FromSWManager)
|
msg.expect("Unexpected SW channel panic in constellation").map(Request::FromSWManager)
|
||||||
}
|
}
|
||||||
|
@ -1228,9 +1209,6 @@ where
|
||||||
Request::Layout(message) => {
|
Request::Layout(message) => {
|
||||||
self.handle_request_from_layout(message);
|
self.handle_request_from_layout(message);
|
||||||
},
|
},
|
||||||
Request::NetworkListener(message) => {
|
|
||||||
self.handle_request_from_network_listener(message);
|
|
||||||
},
|
|
||||||
Request::FromSWManager(message) => {
|
Request::FromSWManager(message) => {
|
||||||
self.handle_request_from_swmanager(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) {
|
fn handle_request_from_swmanager(&mut self, message: SWManagerMsg) {
|
||||||
match message {
|
match message {
|
||||||
SWManagerMsg::PostMessageToClient => {
|
SWManagerMsg::PostMessageToClient => {
|
||||||
|
@ -1615,10 +1573,6 @@ where
|
||||||
FromScriptMsg::DiscardTopLevelBrowsingContext => {
|
FromScriptMsg::DiscardTopLevelBrowsingContext => {
|
||||||
self.handle_close_top_level_browsing_context(source_top_ctx_id);
|
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) => {
|
FromScriptMsg::ScriptLoadedURLInIFrame(load_info) => {
|
||||||
self.handle_script_loaded_url_in_iframe_msg(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
|
// 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
|
// 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
|
// a child being added to the parent browsing context. This message is never
|
||||||
|
|
|
@ -11,7 +11,6 @@ mod browsingcontext;
|
||||||
mod constellation;
|
mod constellation;
|
||||||
mod event_loop;
|
mod event_loop;
|
||||||
mod logging;
|
mod logging;
|
||||||
mod network_listener;
|
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
mod sandboxing;
|
mod sandboxing;
|
||||||
mod serviceworker;
|
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::ScheduleBroadcast(..) => target!("ScheduleBroadcast"),
|
||||||
Self::ForwardToEmbedder(msg) => msg.log_target(),
|
Self::ForwardToEmbedder(msg) => msg.log_target(),
|
||||||
Self::InitiateNavigateRequest(..) => target!("InitiateNavigateRequest"),
|
|
||||||
Self::BroadcastStorageEvent(..) => target!("BroadcastStorageEvent"),
|
Self::BroadcastStorageEvent(..) => target!("BroadcastStorageEvent"),
|
||||||
Self::ChangeRunningAnimationsState(..) => target!("ChangeRunningAnimationsState"),
|
Self::ChangeRunningAnimationsState(..) => target!("ChangeRunningAnimationsState"),
|
||||||
Self::CreateCanvasPaintThread(..) => target!("CreateCanvasPaintThread"),
|
Self::CreateCanvasPaintThread(..) => target!("CreateCanvasPaintThread"),
|
||||||
|
|
|
@ -661,6 +661,7 @@ impl RemoteWebFontDownloader {
|
||||||
&core_resource_thread_clone,
|
&core_resource_thread_clone,
|
||||||
request,
|
request,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
Box::new(move |response_message| {
|
Box::new(move |response_message| {
|
||||||
match downloader.handle_web_font_fetch_message(response_message) {
|
match downloader.handle_web_font_fetch_message(response_message) {
|
||||||
DownloaderResponseResult::InProcess => {},
|
DownloaderResponseResult::InProcess => {},
|
||||||
|
|
|
@ -28,8 +28,8 @@ use net_traits::request::{
|
||||||
};
|
};
|
||||||
use net_traits::response::{Response, ResponseBody, ResponseType};
|
use net_traits::response::{Response, ResponseBody, ResponseType};
|
||||||
use net_traits::{
|
use net_traits::{
|
||||||
FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
|
set_default_accept_language, FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceAttribute,
|
||||||
ResourceTimeValue, ResourceTimingType,
|
ResourceFetchTiming, ResourceTimeValue, ResourceTimingType,
|
||||||
};
|
};
|
||||||
use rustls_pki_types::CertificateDer;
|
use rustls_pki_types::CertificateDer;
|
||||||
use serde::{Deserialize, Serialize};
|
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::cors_cache::CorsCache;
|
||||||
use crate::fetch::headers::determine_nosniff;
|
use crate::fetch::headers::determine_nosniff;
|
||||||
use crate::filemanager_thread::FileManager;
|
use crate::filemanager_thread::FileManager;
|
||||||
use crate::http_loader::{
|
use crate::http_loader::{determine_requests_referrer, http_fetch, set_default_accept, HttpState};
|
||||||
determine_requests_referrer, http_fetch, set_default_accept, set_default_accept_language,
|
|
||||||
HttpState,
|
|
||||||
};
|
|
||||||
use crate::protocols::ProtocolRegistry;
|
use crate::protocols::ProtocolRegistry;
|
||||||
use crate::subresource_integrity::is_response_integrity_valid;
|
use crate::subresource_integrity::is_response_integrity_valid;
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ use net_traits::response::{HttpsState, Response, ResponseBody, ResponseType};
|
||||||
use net_traits::{
|
use net_traits::{
|
||||||
CookieSource, FetchMetadata, NetworkError, RedirectEndValue, RedirectStartValue,
|
CookieSource, FetchMetadata, NetworkError, RedirectEndValue, RedirectStartValue,
|
||||||
ReferrerPolicy, ResourceAttribute, ResourceFetchTiming, ResourceTimeValue,
|
ReferrerPolicy, ResourceAttribute, ResourceFetchTiming, ResourceTimeValue,
|
||||||
|
DOCUMENT_ACCEPT_HEADER_VALUE,
|
||||||
};
|
};
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||||
|
@ -77,10 +78,6 @@ use crate::hsts::HstsList;
|
||||||
use crate::http_cache::{CacheKey, HttpCache};
|
use crate::http_cache::{CacheKey, HttpCache};
|
||||||
use crate::resource_thread::{AuthCache, AuthCacheEntry};
|
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.
|
/// The various states an entry of the HttpCache can be in.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum HttpCacheEntryState {
|
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>
|
/// <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> {
|
fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
|
||||||
// Step 1
|
// Step 1
|
||||||
|
|
|
@ -141,6 +141,7 @@ impl DocumentLoader {
|
||||||
fetch_async(
|
fetch_async(
|
||||||
&self.resource_threads.core_thread,
|
&self.resource_threads.core_thread,
|
||||||
request,
|
request,
|
||||||
|
None, /* response_init */
|
||||||
Some(canceller),
|
Some(canceller),
|
||||||
callback,
|
callback,
|
||||||
);
|
);
|
||||||
|
|
|
@ -3262,6 +3262,7 @@ impl GlobalScope {
|
||||||
fetch_async(
|
fetch_async(
|
||||||
&self.core_resource_thread(),
|
&self.core_resource_thread(),
|
||||||
request_builder,
|
request_builder,
|
||||||
|
None,
|
||||||
cancellation_receiver,
|
cancellation_receiver,
|
||||||
network_listener.into_callback(),
|
network_listener.into_callback(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -48,6 +48,7 @@ mod mem;
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
pub(crate) mod messaging;
|
pub(crate) mod messaging;
|
||||||
mod microtask;
|
mod microtask;
|
||||||
|
mod navigation;
|
||||||
mod network_listener;
|
mod network_listener;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mod realms;
|
mod realms;
|
||||||
|
|
|
@ -13,6 +13,7 @@ use crossbeam_channel::{select, Receiver, SendError, Sender};
|
||||||
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg};
|
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg};
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use net_traits::image_cache::PendingImageResponse;
|
use net_traits::image_cache::PendingImageResponse;
|
||||||
|
use net_traits::FetchResponseMsg;
|
||||||
use profile_traits::mem::{self as profile_mem, OpaqueSender, ReportsChan};
|
use profile_traits::mem::{self as profile_mem, OpaqueSender, ReportsChan};
|
||||||
use profile_traits::time::{self as profile_time};
|
use profile_traits::time::{self as profile_time};
|
||||||
use script_traits::{ConstellationControlMsg, LayoutMsg, Painter, ScriptMsg};
|
use script_traits::{ConstellationControlMsg, LayoutMsg, Painter, ScriptMsg};
|
||||||
|
@ -49,7 +50,6 @@ impl MixedMessage {
|
||||||
match self {
|
match self {
|
||||||
MixedMessage::FromConstellation(ref inner_msg) => match *inner_msg {
|
MixedMessage::FromConstellation(ref inner_msg) => match *inner_msg {
|
||||||
ConstellationControlMsg::StopDelayingLoadEventsMode(id) => Some(id),
|
ConstellationControlMsg::StopDelayingLoadEventsMode(id) => Some(id),
|
||||||
ConstellationControlMsg::NavigationResponse(id, _) => Some(id),
|
|
||||||
ConstellationControlMsg::AttachLayout(ref new_layout_info) => new_layout_info
|
ConstellationControlMsg::AttachLayout(ref new_layout_info) => new_layout_info
|
||||||
.parent_info
|
.parent_info
|
||||||
.or(Some(new_layout_info.new_pipeline_id)),
|
.or(Some(new_layout_info.new_pipeline_id)),
|
||||||
|
@ -95,6 +95,7 @@ impl MixedMessage {
|
||||||
pipeline_id
|
pipeline_id
|
||||||
},
|
},
|
||||||
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(_)) => None,
|
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(_)) => None,
|
||||||
|
MainThreadScriptMsg::NavigationResponse { pipeline_id, .. } => Some(pipeline_id),
|
||||||
MainThreadScriptMsg::WorkletLoaded(pipeline_id) => Some(pipeline_id),
|
MainThreadScriptMsg::WorkletLoaded(pipeline_id) => Some(pipeline_id),
|
||||||
MainThreadScriptMsg::RegisterPaintWorklet { pipeline_id, .. } => Some(pipeline_id),
|
MainThreadScriptMsg::RegisterPaintWorklet { pipeline_id, .. } => Some(pipeline_id),
|
||||||
MainThreadScriptMsg::Inactive => None,
|
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
|
/// Notifies the script thread that a new worklet has been loaded, and thus the page should be
|
||||||
/// reflowed.
|
/// reflowed.
|
||||||
WorkletLoaded(PipelineId),
|
WorkletLoaded(PipelineId),
|
||||||
|
NavigationResponse {
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
message: Box<FetchResponseMsg>,
|
||||||
|
},
|
||||||
/// Notifies the script thread that a new paint worklet has been registered.
|
/// Notifies the script thread that a new paint worklet has been registered.
|
||||||
RegisterPaintWorklet {
|
RegisterPaintWorklet {
|
||||||
pipeline_id: PipelineId,
|
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 metrics::{PaintTimeMetrics, MAX_TASK_NS};
|
||||||
use mime::{self, Mime};
|
use mime::{self, Mime};
|
||||||
use net_traits::image_cache::{ImageCache, PendingImageResponse};
|
use net_traits::image_cache::{ImageCache, PendingImageResponse};
|
||||||
use net_traits::request::{
|
use net_traits::request::{Referrer, RequestId};
|
||||||
CredentialsMode, Destination, RedirectMode, RequestBuilder, RequestId, RequestMode,
|
use net_traits::response::ResponseInit;
|
||||||
};
|
|
||||||
use net_traits::storage_thread::StorageType;
|
use net_traits::storage_thread::StorageType;
|
||||||
use net_traits::{
|
use net_traits::{
|
||||||
FetchMetadata, FetchResponseListener, FetchResponseMsg, Metadata, NetworkError,
|
FetchMetadata, FetchResponseListener, FetchResponseMsg, Metadata, NetworkError,
|
||||||
|
@ -140,12 +139,12 @@ use crate::dom::window::Window;
|
||||||
use crate::dom::windowproxy::{CreatorBrowsingContextInfo, WindowProxy};
|
use crate::dom::windowproxy::{CreatorBrowsingContextInfo, WindowProxy};
|
||||||
use crate::dom::worklet::WorkletThreadPool;
|
use crate::dom::worklet::WorkletThreadPool;
|
||||||
use crate::dom::workletglobalscope::WorkletGlobalScopeInit;
|
use crate::dom::workletglobalscope::WorkletGlobalScopeInit;
|
||||||
use crate::fetch::FetchCanceller;
|
|
||||||
use crate::messaging::{
|
use crate::messaging::{
|
||||||
CommonScriptMsg, MainThreadScriptMsg, MixedMessage, ScriptEventLoopSender,
|
CommonScriptMsg, MainThreadScriptMsg, MixedMessage, ScriptEventLoopSender,
|
||||||
ScriptThreadReceivers, ScriptThreadSenders,
|
ScriptThreadReceivers, ScriptThreadSenders,
|
||||||
};
|
};
|
||||||
use crate::microtask::{Microtask, MicrotaskQueue};
|
use crate::microtask::{Microtask, MicrotaskQueue};
|
||||||
|
use crate::navigation::{InProgressLoad, NavigationListener};
|
||||||
use crate::realms::enter_realm;
|
use crate::realms::enter_realm;
|
||||||
use crate::script_module::ScriptFetchOptions;
|
use crate::script_module::ScriptFetchOptions;
|
||||||
use crate::script_runtime::{
|
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,
|
// We borrow the incomplete parser contexts mutably during parsing,
|
||||||
// which is fine except that parsing can trigger evaluation,
|
// which is fine except that parsing can trigger evaluation,
|
||||||
// which can trigger GC, and so we can end up tracing the script
|
// 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 top_level_browsing_context_id = state.top_level_browsing_context_id;
|
||||||
let parent_info = state.parent_info;
|
let parent_info = state.parent_info;
|
||||||
let opener = state.opener;
|
let opener = state.opener;
|
||||||
let secure = load_data.inherited_secure_context;
|
|
||||||
let memory_profiler_sender = state.memory_profiler_sender.clone();
|
let memory_profiler_sender = state.memory_profiler_sender.clone();
|
||||||
let window_size = state.window_size;
|
let window_size = state.window_size;
|
||||||
|
|
||||||
|
@ -507,18 +428,16 @@ impl ScriptThreadFactory for ScriptThread {
|
||||||
let mut failsafe = ScriptMemoryFailsafe::new(&script_thread);
|
let mut failsafe = ScriptMemoryFailsafe::new(&script_thread);
|
||||||
|
|
||||||
let origin = MutableOrigin::new(load_data.url.origin());
|
let origin = MutableOrigin::new(load_data.url.origin());
|
||||||
let new_load = InProgressLoad::new(
|
script_thread.pre_page_load(InProgressLoad::new(
|
||||||
id,
|
id,
|
||||||
browsing_context_id,
|
browsing_context_id,
|
||||||
top_level_browsing_context_id,
|
top_level_browsing_context_id,
|
||||||
parent_info,
|
parent_info,
|
||||||
opener,
|
opener,
|
||||||
window_size,
|
window_size,
|
||||||
load_data.url.clone(),
|
|
||||||
origin,
|
origin,
|
||||||
secure,
|
load_data,
|
||||||
);
|
));
|
||||||
script_thread.pre_page_load(new_load, load_data);
|
|
||||||
|
|
||||||
let reporter_name = format!("script-reporter-{:?}", id);
|
let reporter_name = format!("script-reporter-{:?}", id);
|
||||||
memory_profiler_sender.run_with_memory_reporting(
|
memory_profiler_sender.run_with_memory_reporting(
|
||||||
|
@ -1822,20 +1741,6 @@ impl ScriptThread {
|
||||||
ConstellationControlMsg::StopDelayingLoadEventsMode(pipeline_id) => {
|
ConstellationControlMsg::StopDelayingLoadEventsMode(pipeline_id) => {
|
||||||
self.handle_stop_delaying_load_events_mode(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(
|
ConstellationControlMsg::NavigateIframe(
|
||||||
parent_pipeline_id,
|
parent_pipeline_id,
|
||||||
browsing_context_id,
|
browsing_context_id,
|
||||||
|
@ -2079,6 +1984,12 @@ impl ScriptThread {
|
||||||
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(chan)) => {
|
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(chan)) => {
|
||||||
self.collect_reports(chan)
|
self.collect_reports(chan)
|
||||||
},
|
},
|
||||||
|
MainThreadScriptMsg::NavigationResponse {
|
||||||
|
pipeline_id,
|
||||||
|
message,
|
||||||
|
} => {
|
||||||
|
self.handle_navigation_response(pipeline_id, *message);
|
||||||
|
},
|
||||||
MainThreadScriptMsg::WorkletLoaded(pipeline_id) => {
|
MainThreadScriptMsg::WorkletLoaded(pipeline_id) => {
|
||||||
self.handle_worklet_loaded(pipeline_id)
|
self.handle_worklet_loaded(pipeline_id)
|
||||||
},
|
},
|
||||||
|
@ -2484,6 +2395,7 @@ impl ScriptThread {
|
||||||
} = new_layout_info;
|
} = new_layout_info;
|
||||||
|
|
||||||
// Kick off the fetch for the new resource.
|
// Kick off the fetch for the new resource.
|
||||||
|
let url = load_data.url.clone();
|
||||||
let new_load = InProgressLoad::new(
|
let new_load = InProgressLoad::new(
|
||||||
new_pipeline_id,
|
new_pipeline_id,
|
||||||
browsing_context_id,
|
browsing_context_id,
|
||||||
|
@ -2491,16 +2403,15 @@ impl ScriptThread {
|
||||||
parent_info,
|
parent_info,
|
||||||
opener,
|
opener,
|
||||||
window_size,
|
window_size,
|
||||||
load_data.url.clone(),
|
|
||||||
origin,
|
origin,
|
||||||
load_data.inherited_secure_context,
|
load_data,
|
||||||
);
|
);
|
||||||
if load_data.url.as_str() == "about:blank" {
|
if url.as_str() == "about:blank" {
|
||||||
self.start_page_load_about_blank(new_load, load_data);
|
self.start_page_load_about_blank(new_load);
|
||||||
} else if load_data.url.as_str() == "about:srcdoc" {
|
} else if url.as_str() == "about:srcdoc" {
|
||||||
self.page_load_about_srcdoc(new_load, load_data);
|
self.page_load_about_srcdoc(new_load);
|
||||||
} else {
|
} else {
|
||||||
self.pre_page_load(new_load, load_data);
|
self.pre_page_load(new_load);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3118,7 +3029,7 @@ impl ScriptThread {
|
||||||
}
|
}
|
||||||
debug!(
|
debug!(
|
||||||
"ScriptThread: loading {} on pipeline {:?}",
|
"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"
|
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(),
|
self.player_context.clone(),
|
||||||
#[cfg(feature = "webgpu")]
|
#[cfg(feature = "webgpu")]
|
||||||
self.gpu_id_hub.clone(),
|
self.gpu_id_hub.clone(),
|
||||||
incomplete.inherited_secure_context,
|
incomplete.load_data.inherited_secure_context,
|
||||||
);
|
);
|
||||||
|
|
||||||
let _realm = enter_realm(&*window);
|
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
|
/// 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.
|
/// argument until a notification is received that the fetch is complete.
|
||||||
fn pre_page_load(&self, mut incomplete: InProgressLoad, load_data: LoadData) {
|
fn pre_page_load(&self, mut incomplete: InProgressLoad) {
|
||||||
let id = incomplete.pipeline_id;
|
let context = ParserContext::new(incomplete.pipeline_id, incomplete.load_data.url.clone());
|
||||||
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);
|
|
||||||
self.incomplete_parser_contexts
|
self.incomplete_parser_contexts
|
||||||
.0
|
.0
|
||||||
.borrow_mut()
|
.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);
|
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(
|
fn handle_fetch_metadata(
|
||||||
&self,
|
&self,
|
||||||
id: PipelineId,
|
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`
|
/// Synchronously fetch `about:blank`. Stores the `InProgressLoad`
|
||||||
/// argument until a notification is received that the fetch is complete.
|
/// 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;
|
let id = incomplete.pipeline_id;
|
||||||
|
|
||||||
self.incomplete_loads.borrow_mut().push(incomplete);
|
|
||||||
|
|
||||||
let url = ServoUrl::parse("about:blank").unwrap();
|
let url = ServoUrl::parse("about:blank").unwrap();
|
||||||
let mut context = ParserContext::new(id, url.clone());
|
let mut context = ParserContext::new(id, url.clone());
|
||||||
|
|
||||||
let mut meta = Metadata::default(url);
|
let mut meta = Metadata::default(url);
|
||||||
meta.set_content_type(Some(&mime::TEXT_HTML));
|
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
|
// If this page load is the result of a javascript scheme url, map
|
||||||
// the evaluation result into a response.
|
// the evaluation result into a response.
|
||||||
let chunk = match load_data.js_eval_result {
|
let chunk = match incomplete.load_data.js_eval_result {
|
||||||
Some(JsEvalResult::Ok(content)) => content,
|
Some(JsEvalResult::Ok(ref mut content)) => std::mem::take(content),
|
||||||
Some(JsEvalResult::NoContent) => {
|
Some(JsEvalResult::NoContent) => {
|
||||||
meta.status = http::StatusCode::NO_CONTENT.into();
|
meta.status = http::StatusCode::NO_CONTENT.into();
|
||||||
vec![]
|
vec![]
|
||||||
|
@ -3621,6 +3588,8 @@ impl ScriptThread {
|
||||||
None => vec![],
|
None => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.incomplete_loads.borrow_mut().push(incomplete);
|
||||||
|
|
||||||
let dummy_request_id = RequestId::next();
|
let dummy_request_id = RequestId::next();
|
||||||
context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta)));
|
context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta)));
|
||||||
context.process_response_chunk(dummy_request_id, chunk);
|
context.process_response_chunk(dummy_request_id, chunk);
|
||||||
|
@ -3631,20 +3600,20 @@ impl ScriptThread {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synchronously parse a srcdoc document from a giving HTML string.
|
/// 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 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);
|
self.incomplete_loads.borrow_mut().push(incomplete);
|
||||||
|
|
||||||
let url = ServoUrl::parse("about:srcdoc").unwrap();
|
let mut context = ParserContext::new(id, url);
|
||||||
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 dummy_request_id = RequestId::next();
|
let dummy_request_id = RequestId::next();
|
||||||
context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta)));
|
context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta)));
|
||||||
context.process_response_chunk(dummy_request_id, chunk);
|
context.process_response_chunk(dummy_request_id, chunk);
|
||||||
|
|
|
@ -14,7 +14,7 @@ use base::id::HistoryStateId;
|
||||||
use cookie::Cookie;
|
use cookie::Cookie;
|
||||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||||
use headers::{ContentType, HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
|
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_serde::Serde;
|
||||||
use hyper_util::client::legacy::Error as HyperError;
|
use hyper_util::client::legacy::Error as HyperError;
|
||||||
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
||||||
|
@ -46,6 +46,10 @@ pub mod request;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
pub mod storage_thread;
|
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/)
|
/// An implementation of the [Fetch specification](https://fetch.spec.whatwg.org/)
|
||||||
pub mod fetch {
|
pub mod fetch {
|
||||||
pub mod headers;
|
pub mod headers;
|
||||||
|
@ -197,7 +201,7 @@ pub enum FetchResponseMsg {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FetchResponseMsg {
|
impl FetchResponseMsg {
|
||||||
fn request_id(&self) -> RequestId {
|
pub fn request_id(&self) -> RequestId {
|
||||||
match self {
|
match self {
|
||||||
FetchResponseMsg::ProcessRequestBody(id) |
|
FetchResponseMsg::ProcessRequestBody(id) |
|
||||||
FetchResponseMsg::ProcessRequestEOF(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 {
|
pub trait FetchResponseListener {
|
||||||
fn process_request_body(&mut self, request_id: RequestId);
|
fn process_request_body(&mut self, request_id: RequestId);
|
||||||
fn process_request_eof(&mut self, request_id: RequestId);
|
fn process_request_eof(&mut self, request_id: RequestId);
|
||||||
|
@ -511,6 +524,7 @@ pub enum CoreResourceMsg {
|
||||||
enum ToFetchThreadMessage {
|
enum ToFetchThreadMessage {
|
||||||
StartFetch(
|
StartFetch(
|
||||||
/* request_builder */ RequestBuilder,
|
/* request_builder */ RequestBuilder,
|
||||||
|
/* response_init */ Option<ResponseInit>,
|
||||||
/* cancel_chan */ Option<IpcReceiver<()>>,
|
/* cancel_chan */ Option<IpcReceiver<()>>,
|
||||||
/* callback */ BoxedFetchCallback,
|
/* callback */ BoxedFetchCallback,
|
||||||
),
|
),
|
||||||
|
@ -570,14 +584,29 @@ impl FetchThread {
|
||||||
fn run(&mut self) {
|
fn run(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
match self.receiver.recv().unwrap() {
|
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.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,
|
request_builder,
|
||||||
FetchChannels::ResponseMsg(self.to_fetch_sender.clone(), canceller),
|
FetchChannels::ResponseMsg(self.to_fetch_sender.clone(), canceller),
|
||||||
))
|
),
|
||||||
.unwrap();
|
};
|
||||||
|
|
||||||
|
self.core_resource_thread.send(message).unwrap();
|
||||||
},
|
},
|
||||||
ToFetchThreadMessage::FetchResponse(fetch_response_msg) => {
|
ToFetchThreadMessage::FetchResponse(fetch_response_msg) => {
|
||||||
let request_id = fetch_response_msg.request_id();
|
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(
|
pub fn fetch_async(
|
||||||
core_resource_thread: &CoreResourceThread,
|
core_resource_thread: &CoreResourceThread,
|
||||||
request: RequestBuilder,
|
request: RequestBuilder,
|
||||||
|
response_init: Option<ResponseInit>,
|
||||||
canceller: Option<IpcReceiver<()>>,
|
canceller: Option<IpcReceiver<()>>,
|
||||||
callback: BoxedFetchCallback,
|
callback: BoxedFetchCallback,
|
||||||
) {
|
) {
|
||||||
static FETCH_THREAD: OnceLock<Sender<ToFetchThreadMessage>> = OnceLock::new();
|
|
||||||
let _ = FETCH_THREAD
|
let _ = FETCH_THREAD
|
||||||
.get_or_init(|| FetchThread::spawn(core_resource_thread))
|
.get_or_init(|| FetchThread::spawn(core_resource_thread))
|
||||||
.send(ToFetchThreadMessage::StartFetch(
|
.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()
|
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> =
|
pub static PRIVILEGED_SECRET: LazyLock<u32> =
|
||||||
LazyLock::new(|| servo_rand::ServoRng::default().next_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::image_cache::ImageCache;
|
||||||
use net_traits::request::{Referrer, RequestBody};
|
use net_traits::request::{Referrer, RequestBody};
|
||||||
use net_traits::storage_thread::StorageType;
|
use net_traits::storage_thread::StorageType;
|
||||||
use net_traits::{FetchResponseMsg, ReferrerPolicy, ResourceThreads};
|
use net_traits::{ReferrerPolicy, ResourceThreads};
|
||||||
use pixels::{Image, PixelFormat};
|
use pixels::{Image, PixelFormat};
|
||||||
use profile_traits::{mem, time as profile_time};
|
use profile_traits::{mem, time as profile_time};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
@ -286,16 +286,12 @@ pub enum UpdatePipelineIdReason {
|
||||||
|
|
||||||
/// Messages sent from the constellation or layout to the script thread.
|
/// Messages sent from the constellation or layout to the script thread.
|
||||||
// FIXME: https://github.com/servo/servo/issues/34591
|
// FIXME: https://github.com/servo/servo/issues/34591
|
||||||
#[expect(clippy::large_enum_variant)]
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub enum ConstellationControlMsg {
|
pub enum ConstellationControlMsg {
|
||||||
/// Takes the associated window proxy out of "delaying-load-events-mode",
|
/// Takes the associated window proxy out of "delaying-load-events-mode",
|
||||||
/// used if a scheduled navigated was refused by the embedder.
|
/// used if a scheduled navigated was refused by the embedder.
|
||||||
/// <https://html.spec.whatwg.org/multipage/#delaying-load-events-mode>
|
/// <https://html.spec.whatwg.org/multipage/#delaying-load-events-mode>
|
||||||
StopDelayingLoadEventsMode(PipelineId),
|
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
|
/// Gives a channel and ID to a layout, as well as the ID of that layout's parent
|
||||||
AttachLayout(NewLayoutInfo),
|
AttachLayout(NewLayoutInfo),
|
||||||
/// Window resized. Sends a DOM event eventually, but first we combine events.
|
/// Window resized. Sends a DOM event eventually, but first we combine events.
|
||||||
|
@ -413,7 +409,6 @@ impl fmt::Debug for ConstellationControlMsg {
|
||||||
use self::ConstellationControlMsg::*;
|
use self::ConstellationControlMsg::*;
|
||||||
let variant = match *self {
|
let variant = match *self {
|
||||||
StopDelayingLoadEventsMode(..) => "StopDelayingLoadsEventMode",
|
StopDelayingLoadEventsMode(..) => "StopDelayingLoadsEventMode",
|
||||||
NavigationResponse(..) => "NavigationResponse",
|
|
||||||
AttachLayout(..) => "AttachLayout",
|
AttachLayout(..) => "AttachLayout",
|
||||||
Resize(..) => "Resize",
|
Resize(..) => "Resize",
|
||||||
ThemeChange(..) => "ThemeChange",
|
ThemeChange(..) => "ThemeChange",
|
||||||
|
|
|
@ -17,7 +17,6 @@ use embedder_traits::{EmbedderMsg, MediaSessionEvent};
|
||||||
use euclid::default::Size2D as UntypedSize2D;
|
use euclid::default::Size2D as UntypedSize2D;
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||||
use net_traits::request::RequestBuilder;
|
|
||||||
use net_traits::storage_thread::StorageType;
|
use net_traits::storage_thread::StorageType;
|
||||||
use net_traits::CoreResourceMsg;
|
use net_traits::CoreResourceMsg;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -128,9 +127,6 @@ pub enum ScriptMsg {
|
||||||
ScheduleBroadcast(BroadcastChannelRouterId, BroadcastMsg),
|
ScheduleBroadcast(BroadcastChannelRouterId, BroadcastMsg),
|
||||||
/// Forward a message to the embedder.
|
/// Forward a message to the embedder.
|
||||||
ForwardToEmbedder(EmbedderMsg),
|
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.
|
/// Broadcast a storage event to every same-origin pipeline.
|
||||||
/// The strings are key, old value and new value.
|
/// The strings are key, old value and new value.
|
||||||
BroadcastStorageEvent(
|
BroadcastStorageEvent(
|
||||||
|
@ -271,7 +267,6 @@ impl fmt::Debug for ScriptMsg {
|
||||||
NewBroadcastChannelNameInRouter(..) => "NewBroadcastChannelNameInRouter",
|
NewBroadcastChannelNameInRouter(..) => "NewBroadcastChannelNameInRouter",
|
||||||
ScheduleBroadcast(..) => "ScheduleBroadcast",
|
ScheduleBroadcast(..) => "ScheduleBroadcast",
|
||||||
ForwardToEmbedder(..) => "ForwardToEmbedder",
|
ForwardToEmbedder(..) => "ForwardToEmbedder",
|
||||||
InitiateNavigateRequest(..) => "InitiateNavigateRequest",
|
|
||||||
BroadcastStorageEvent(..) => "BroadcastStorageEvent",
|
BroadcastStorageEvent(..) => "BroadcastStorageEvent",
|
||||||
ChangeRunningAnimationsState(..) => "ChangeRunningAnimationsState",
|
ChangeRunningAnimationsState(..) => "ChangeRunningAnimationsState",
|
||||||
CreateCanvasPaintThread(..) => "CreateCanvasPaintThread",
|
CreateCanvasPaintThread(..) => "CreateCanvasPaintThread",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue