aboutsummaryrefslogtreecommitdiffstats
path: root/components/net/resource_task.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/net/resource_task.rs')
-rw-r--r--components/net/resource_task.rs127
1 files changed, 106 insertions, 21 deletions
diff --git a/components/net/resource_task.rs b/components/net/resource_task.rs
index 529f79b448e..caf73c4a976 100644
--- a/components/net/resource_task.rs
+++ b/components/net/resource_task.rs
@@ -19,10 +19,12 @@ use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use mime_classifier::{ApacheBugFlag, MIMEClassifier, NoSniffFlag};
use net_traits::ProgressMsg::Done;
use net_traits::{AsyncResponseTarget, Metadata, ProgressMsg, ResourceTask, ResponseAction};
-use net_traits::{ControlMsg, CookieSource, LoadConsumer, LoadData, LoadResponse};
+use net_traits::{ControlMsg, CookieSource, LoadConsumer, LoadData, LoadResponse, ResourceId};
use std::borrow::ToOwned;
use std::boxed::FnBox;
-use std::sync::mpsc::{Sender, channel};
+use std::cell::Cell;
+use std::collections::HashMap;
+use std::sync::mpsc::{Receiver, Sender, channel};
use std::sync::{Arc, RwLock};
use url::Url;
use util::opts;
@@ -146,6 +148,7 @@ pub fn new_resource_task(user_agent: String,
};
let (setup_chan, setup_port) = ipc::channel().unwrap();
+ let setup_chan_clone = setup_chan.clone();
spawn_named("ResourceManager".to_owned(), move || {
let resource_manager = ResourceManager::new(
user_agent, hsts_preload, devtools_chan
@@ -155,8 +158,7 @@ pub fn new_resource_task(user_agent: String,
from_client: setup_port,
resource_manager: resource_manager
};
-
- channel_manager.start();
+ channel_manager.start(setup_chan_clone);
});
setup_chan
}
@@ -167,28 +169,85 @@ struct ResourceChannelManager {
}
impl ResourceChannelManager {
- fn start(&mut self) {
+ fn start(&mut self, control_sender: ResourceTask) {
loop {
match self.from_client.recv().unwrap() {
- ControlMsg::Load(load_data, consumer) => {
- self.resource_manager.load(load_data, consumer)
- }
- ControlMsg::SetCookiesForUrl(request, cookie_list, source) => {
- self.resource_manager.set_cookies_for_url(request, cookie_list, source)
- }
+ ControlMsg::Load(load_data, consumer, id_sender) =>
+ self.resource_manager.load(load_data, consumer, id_sender, control_sender.clone()),
+ ControlMsg::SetCookiesForUrl(request, cookie_list, source) =>
+ self.resource_manager.set_cookies_for_url(request, cookie_list, source),
ControlMsg::GetCookiesForUrl(url, consumer, source) => {
let cookie_jar = &self.resource_manager.cookie_storage;
let mut cookie_jar = cookie_jar.write().unwrap();
consumer.send(cookie_jar.cookies_for_url(&url, source)).unwrap();
}
- ControlMsg::Exit => {
- break
+ ControlMsg::Cancel(res_id) => {
+ if let Some(cancel_sender) = self.resource_manager.cancel_load_map.get(&res_id) {
+ let _ = cancel_sender.send(());
+ }
+ self.resource_manager.cancel_load_map.remove(&res_id);
}
+ ControlMsg::Exit => break,
}
}
}
}
+/// The optional resources required by the `CancellationListener`
+pub struct CancellableResource {
+ /// The receiver which receives a message on load cancellation
+ cancel_receiver: Receiver<()>,
+ /// The `CancellationListener` is unique to this `ResourceId`
+ resource_id: ResourceId,
+ /// If we haven't initiated any cancel requests, then the loaders ask
+ /// the listener to remove the `ResourceId` in the `HashMap` of
+ /// `ResourceManager` once they finish loading
+ resource_task: ResourceTask,
+}
+
+/// A listener which is basically a wrapped optional receiver which looks
+/// for the load cancellation message. Some of the loading processes always keep
+/// an eye out for this message and stop loading stuff once they receive it.
+pub struct CancellationListener {
+ /// We'll be needing the resources only if we plan to cancel it
+ cancel_resource: Option<CancellableResource>,
+ /// This lets us know whether the request has already been cancelled
+ cancel_status: Cell<bool>,
+}
+
+impl CancellationListener {
+ pub fn new(resources: Option<CancellableResource>) -> CancellationListener {
+ CancellationListener {
+ cancel_resource: resources,
+ cancel_status: Cell::new(false),
+ }
+ }
+
+ pub fn is_cancelled(&self) -> bool {
+ match self.cancel_resource {
+ Some(ref resource) => {
+ match resource.cancel_receiver.try_recv() {
+ Ok(_) => {
+ self.cancel_status.set(true);
+ true
+ },
+ Err(_) => self.cancel_status.get(),
+ }
+ },
+ None => false, // channel doesn't exist!
+ }
+ }
+}
+
+impl Drop for CancellationListener {
+ fn drop(&mut self) {
+ if let Some(ref resource) = self.cancel_resource {
+ // Ensure that the resource manager stops tracking this request now that it's terminated.
+ let _ = resource.resource_task.send(ControlMsg::Cancel(resource.resource_id));
+ }
+ }
+}
+
pub struct ResourceManager {
user_agent: String,
cookie_storage: Arc<RwLock<CookieStorage>>,
@@ -196,6 +255,8 @@ pub struct ResourceManager {
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
hsts_list: Arc<RwLock<HSTSList>>,
connector: Arc<Pool<Connector>>,
+ cancel_load_map: HashMap<ResourceId, Sender<()>>,
+ next_resource_id: ResourceId,
}
impl ResourceManager {
@@ -209,11 +270,11 @@ impl ResourceManager {
devtools_chan: devtools_channel,
hsts_list: Arc::new(RwLock::new(hsts_list)),
connector: create_http_connector(),
+ cancel_load_map: HashMap::new(),
+ next_resource_id: ResourceId(0),
}
}
-}
-impl ResourceManager {
fn set_cookies_for_url(&mut self, request: Url, cookie_list: String, source: CookieSource) {
let header = Header::parse_header(&[cookie_list.into_bytes()]);
if let Ok(SetCookie(cookies)) = header {
@@ -227,15 +288,36 @@ impl ResourceManager {
}
}
- fn load(&mut self, load_data: LoadData, consumer: LoadConsumer) {
+ fn load(&mut self,
+ load_data: LoadData,
+ consumer: LoadConsumer,
+ id_sender: Option<IpcSender<ResourceId>>,
+ resource_task: ResourceTask) {
- fn from_factory(factory: fn(LoadData, LoadConsumer, Arc<MIMEClassifier>))
- -> Box<FnBox(LoadData, LoadConsumer, Arc<MIMEClassifier>) + Send> {
- box move |load_data, senders, classifier| {
- factory(load_data, senders, classifier)
+ fn from_factory(factory: fn(LoadData, LoadConsumer, Arc<MIMEClassifier>, CancellationListener))
+ -> Box<FnBox(LoadData,
+ LoadConsumer,
+ Arc<MIMEClassifier>,
+ CancellationListener) + Send> {
+ box move |load_data, senders, classifier, cancel_listener| {
+ factory(load_data, senders, classifier, cancel_listener)
}
}
+ let cancel_resource = id_sender.map(|sender| {
+ let current_res_id = self.next_resource_id;
+ let _ = sender.send(current_res_id);
+ let (cancel_sender, cancel_receiver) = channel();
+ self.cancel_load_map.insert(current_res_id, cancel_sender);
+ self.next_resource_id.0 += 1;
+ CancellableResource {
+ cancel_receiver: cancel_receiver,
+ resource_id: current_res_id,
+ resource_task: resource_task,
+ }
+ });
+
+ let cancel_listener = CancellationListener::new(cancel_resource);
let loader = match &*load_data.url.scheme {
"file" => from_factory(file_loader::factory),
"http" | "https" | "view-source" =>
@@ -254,6 +336,9 @@ impl ResourceManager {
};
debug!("resource_task: loading url: {}", load_data.url.serialize());
- loader.call_box((load_data, consumer, self.mime_classifier.clone()));
+ loader.call_box((load_data,
+ consumer,
+ self.mime_classifier.clone(),
+ cancel_listener));
}
}