mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
This is the last big change necessary to create the `constellation_traits` crate. This moves the data structure for messages that originate from the `ScriptThread` and are sent to the `Contellation` to `constellation_traits`, effectively splitting `script_traits` in half. Before, `script_traits` was responsible for exposing the API of both the `ScriptThread` and the `Constellation` to the rest of Servo. - Data structures that are used by `ScriptToConstellationMsg` are moved to `constellation_traits`. The dependency graph looks a bit like this: `script_layout_interface` depends on `script_traits` depends on `constellation_traits` depends on `embedder_traits`. - Data structures that are used in the embedding layer (`UntrustedNodeAddress`, `CompositorHitTestResult`, `TouchEventResult` and `AnimationState`) are moved to embedder_traits, to avoid a dependency cycle between `webrender_traits` and `constellation_traits`. - Types dealing with MessagePorts and serialization are moved to `constellation_traits::message_port`. Testing: This is covered by existing tests as it just moves types around. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Signed-off-by: Martin Robinson <mrobinson@igalia.com>
233 lines
8.2 KiB
Rust
233 lines
8.2 KiB
Rust
/* 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, WebViewId};
|
|
use constellation_traits::LoadData;
|
|
use content_security_policy::Destination;
|
|
use crossbeam_channel::Sender;
|
|
use embedder_traits::ViewportDetails;
|
|
use http::header;
|
|
use net_traits::request::{
|
|
CredentialsMode, InsecureRequestsPolicy, RedirectMode, RequestBuilder, RequestMode,
|
|
};
|
|
use net_traits::response::ResponseInit;
|
|
use net_traits::{
|
|
BoxedFetchCallback, CoreResourceThread, DOCUMENT_ACCEPT_HEADER_VALUE, FetchResponseMsg,
|
|
Metadata, fetch_async, set_default_accept_language,
|
|
};
|
|
use script_traits::DocumentActivity;
|
|
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>,
|
|
) {
|
|
fetch_async(
|
|
core_resource_thread,
|
|
self.request_builder.clone(),
|
|
response_init,
|
|
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(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) webview_id: WebViewId,
|
|
/// 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) viewport_details: ViewportDetails,
|
|
/// 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,
|
|
webview_id: WebViewId,
|
|
parent_info: Option<PipelineId>,
|
|
opener: Option<BrowsingContextId>,
|
|
viewport_details: ViewportDetails,
|
|
origin: MutableOrigin,
|
|
load_data: LoadData,
|
|
) -> InProgressLoad {
|
|
let url = load_data.url.clone();
|
|
InProgressLoad {
|
|
pipeline_id: id,
|
|
browsing_context_id,
|
|
webview_id,
|
|
parent_info,
|
|
opener,
|
|
viewport_details,
|
|
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 webview_id = self.webview_id;
|
|
let mut request_builder = RequestBuilder::new(
|
|
Some(webview_id),
|
|
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))
|
|
.referrer_policy(self.load_data.referrer_policy)
|
|
.insecure_requests_policy(
|
|
self.load_data
|
|
.inherited_insecure_requests_policy
|
|
.unwrap_or(InsecureRequestsPolicy::DoNotUpgrade),
|
|
)
|
|
.has_trustworthy_ancestor_origin(self.load_data.has_trustworthy_ancestor_origin)
|
|
.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
|
|
}
|
|
}
|