mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
Auto merge of #15354 - cynicaldevil:manual-redirect, r=jdm
Redirect document loads manually <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #14596 . r? @jdm I ran some tests at random from the `navigating-across-documents` folder, and they are passing. <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/15354) <!-- Reviewable:end -->
This commit is contained in:
commit
eac4f407e2
17 changed files with 376 additions and 52 deletions
|
@ -21,6 +21,7 @@ devtools_traits = {path = "../devtools_traits"}
|
|||
euclid = "0.11"
|
||||
gfx = {path = "../gfx"}
|
||||
gfx_traits = {path = "../gfx_traits"}
|
||||
hyper = "0.10"
|
||||
ipc-channel = "0.7"
|
||||
itertools = "0.5"
|
||||
layout_traits = {path = "../layout_traits"}
|
||||
|
|
|
@ -91,9 +91,11 @@ use log::{Log, LogLevel, LogLevelFilter, LogMetadata, LogRecord};
|
|||
use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, FrameType, PipelineId};
|
||||
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
|
||||
use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, TraversalDirection};
|
||||
use net_traits::{self, IpcSend, ResourceThreads};
|
||||
use net_traits::{self, IpcSend, FetchResponseMsg, ResourceThreads};
|
||||
use net_traits::pub_domains::reg_host;
|
||||
use net_traits::request::RequestInit;
|
||||
use net_traits::storage_thread::{StorageThreadMsg, StorageType};
|
||||
use network_listener::NetworkListener;
|
||||
use offscreen_gl_context::{GLContextAttributes, GLLimits};
|
||||
use pipeline::{InitialPipelineState, Pipeline};
|
||||
use profile_traits::mem;
|
||||
|
@ -159,6 +161,12 @@ pub struct Constellation<Message, LTF, STF> {
|
|||
/// 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>,
|
||||
|
||||
|
@ -502,6 +510,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
let (ipc_layout_sender, ipc_layout_receiver) = ipc::channel().expect("ipc channel failure");
|
||||
let layout_receiver = route_ipc_receiver_to_new_mpsc_receiver_preserving_errors(ipc_layout_receiver);
|
||||
|
||||
let (network_listener_sender, network_listener_receiver) = channel();
|
||||
|
||||
let swmanager_receiver = route_ipc_receiver_to_new_mpsc_receiver_preserving_errors(swmanager_receiver);
|
||||
|
||||
PipelineNamespace::install(PipelineNamespaceId(0));
|
||||
|
@ -512,6 +522,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
script_receiver: script_receiver,
|
||||
compositor_receiver: compositor_receiver,
|
||||
layout_receiver: layout_receiver,
|
||||
network_listener_sender: network_listener_sender,
|
||||
network_listener_receiver: network_listener_receiver,
|
||||
compositor_proxy: state.compositor_proxy,
|
||||
debugger_chan: state.debugger_chan,
|
||||
devtools_chan: state.devtools_chan,
|
||||
|
@ -797,6 +809,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
Script(FromScriptMsg),
|
||||
Compositor(FromCompositorMsg),
|
||||
Layout(FromLayoutMsg),
|
||||
NetworkListener((PipelineId, FetchResponseMsg)),
|
||||
FromSWManager(SWManagerMsg),
|
||||
}
|
||||
|
||||
|
@ -815,6 +828,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
let receiver_from_script = &self.script_receiver;
|
||||
let receiver_from_compositor = &self.compositor_receiver;
|
||||
let receiver_from_layout = &self.layout_receiver;
|
||||
let receiver_from_network_listener = &self.network_listener_receiver;
|
||||
let receiver_from_swmanager = &self.swmanager_receiver;
|
||||
select! {
|
||||
msg = receiver_from_script.recv() =>
|
||||
|
@ -823,6 +837,10 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
Ok(Request::Compositor(msg.expect("Unexpected compositor channel panic in constellation"))),
|
||||
msg = receiver_from_layout.recv() =>
|
||||
msg.expect("Unexpected layout channel panic in constellation").map(Request::Layout),
|
||||
msg = receiver_from_network_listener.recv() =>
|
||||
Ok(Request::NetworkListener(
|
||||
msg.expect("Unexpected network listener channel panic in constellation")
|
||||
)),
|
||||
msg = receiver_from_swmanager.recv() =>
|
||||
msg.expect("Unexpected panic channel panic in constellation").map(Request::FromSWManager)
|
||||
}
|
||||
|
@ -849,12 +867,31 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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!("Pipeline {:?} 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::OwnSender(sw_sender) => {
|
||||
|
@ -956,6 +993,10 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
FromScriptMsg::PipelineExited(pipeline_id) => {
|
||||
self.handle_pipeline_exited(pipeline_id);
|
||||
}
|
||||
FromScriptMsg::InitiateNavigateRequest(req_init, pipeline_id) => {
|
||||
debug!("constellation got initiate navigate request message");
|
||||
self.handle_navigate_request(req_init, pipeline_id);
|
||||
}
|
||||
FromScriptMsg::ScriptLoadedURLInIFrame(load_info) => {
|
||||
debug!("constellation got iframe URL load message {:?} {:?} {:?}",
|
||||
load_info.info.parent_pipeline_id,
|
||||
|
@ -1484,6 +1525,18 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_navigate_request(&self,
|
||||
req_init: RequestInit,
|
||||
id: PipelineId) {
|
||||
let listener = NetworkListener::new(
|
||||
req_init,
|
||||
id,
|
||||
self.public_resource_threads.clone(),
|
||||
self.network_listener_sender.clone());
|
||||
|
||||
listener.initiate_fetch();
|
||||
}
|
||||
|
||||
// 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 pipeline. This message is never the result of a
|
||||
|
|
|
@ -20,6 +20,7 @@ extern crate euclid;
|
|||
extern crate gaol;
|
||||
extern crate gfx;
|
||||
extern crate gfx_traits;
|
||||
extern crate hyper;
|
||||
extern crate ipc_channel;
|
||||
extern crate itertools;
|
||||
extern crate layout_traits;
|
||||
|
@ -45,6 +46,7 @@ extern crate webvr_traits;
|
|||
mod browsingcontext;
|
||||
mod constellation;
|
||||
mod event_loop;
|
||||
mod network_listener;
|
||||
mod pipeline;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
mod sandboxing;
|
||||
|
|
133
components/constellation/network_listener.rs
Normal file
133
components/constellation/network_listener.rs
Normal file
|
@ -0,0 +1,133 @@
|
|||
/* 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 http://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 hyper::header::Location;
|
||||
use ipc_channel::ipc;
|
||||
use ipc_channel::router::ROUTER;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use net::http_loader::{set_default_accept, set_default_accept_language};
|
||||
use net_traits::{CoreResourceMsg, FetchMetadata, FetchResponseMsg};
|
||||
use net_traits::{IpcSend, NetworkError, ResourceThreads};
|
||||
use net_traits::request::{Destination, RequestInit, Type};
|
||||
use net_traits::response::ResponseInit;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
pub struct NetworkListener {
|
||||
res_init: Option<ResponseInit>,
|
||||
req_init: RequestInit,
|
||||
pipeline_id: PipelineId,
|
||||
resource_threads: ResourceThreads,
|
||||
sender: Sender<(PipelineId, FetchResponseMsg)>,
|
||||
should_send: bool,
|
||||
}
|
||||
|
||||
impl NetworkListener {
|
||||
pub fn new(req_init: RequestInit,
|
||||
pipeline_id: PipelineId,
|
||||
resource_threads: ResourceThreads,
|
||||
sender: Sender<(PipelineId, FetchResponseMsg)>) -> NetworkListener {
|
||||
NetworkListener {
|
||||
res_init: None,
|
||||
req_init,
|
||||
pipeline_id,
|
||||
resource_threads,
|
||||
sender,
|
||||
should_send: false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initiate_fetch(&self) {
|
||||
let (ipc_sender, ipc_receiver) = ipc::channel().expect("Failed to create IPC channel!");
|
||||
|
||||
let mut listener = NetworkListener {
|
||||
res_init: self.res_init.clone(),
|
||||
req_init: self.req_init.clone(),
|
||||
resource_threads: self.resource_threads.clone(),
|
||||
sender: self.sender.clone(),
|
||||
pipeline_id: self.pipeline_id.clone(),
|
||||
should_send: false,
|
||||
};
|
||||
|
||||
let msg = match self.res_init {
|
||||
Some(ref res_init_) => CoreResourceMsg::FetchRedirect(
|
||||
self.req_init.clone(),
|
||||
res_init_.clone(),
|
||||
ipc_sender),
|
||||
None => {
|
||||
set_default_accept(Type::None, Destination::Document, &mut listener.req_init.headers);
|
||||
set_default_accept_language(&mut listener.req_init.headers);
|
||||
|
||||
CoreResourceMsg::Fetch(
|
||||
listener.req_init.clone(),
|
||||
ipc_sender)
|
||||
}
|
||||
};
|
||||
|
||||
ROUTER.add_route(ipc_receiver.to_opaque(), box move |message| {
|
||||
let msg = message.to();
|
||||
match msg {
|
||||
Ok(FetchResponseMsg::ProcessResponse(res)) => listener.check_redirect(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,
|
||||
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.headers {
|
||||
Some(ref headers) if headers.has::<Location>() => {
|
||||
if self.req_init.url_list.is_empty() {
|
||||
self.req_init.url_list.push(self.req_init.url.clone());
|
||||
}
|
||||
self.req_init.url_list.push(metadata.final_url.clone());
|
||||
|
||||
self.req_init.referrer_url = metadata.referrer.clone();
|
||||
self.req_init.referrer_policy = metadata.referrer_policy;
|
||||
|
||||
self.res_init = Some(ResponseInit {
|
||||
url: metadata.final_url.clone(),
|
||||
headers: headers.clone().into_inner(),
|
||||
referrer: metadata.referrer.clone(),
|
||||
});
|
||||
|
||||
self.initiate_fetch();
|
||||
},
|
||||
_ => {
|
||||
// Response should be processed by script thread.
|
||||
self.should_send = true;
|
||||
self.send(FetchResponseMsg::ProcessResponse(Ok(res_metadata.clone())));
|
||||
}
|
||||
};
|
||||
},
|
||||
Err(e) => {
|
||||
self.should_send = true;
|
||||
self.send(FetchResponseMsg::ProcessResponse(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue