From 7bf2e19437ad954bd1d03bb52b86551546ae8ba5 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 3 Jun 2016 13:39:38 -0400 Subject: [PATCH] Make the net monitor panel in FF's devtools show meaningful output. 0) Advertise support for the network monitor in the initial protocol communication. 1) Only notify the developer tools server about the final request in an HTTP transaction. 2) Add timing information for connecting to the HTTP server and sending the HTTP request. 3) Reduce duplication between various networkEventUpdate structures by creating a helper function that merges two JSON structures together. This also corrects the JSON structure so the devtools client interprets the output correctly. 4) Calculate various header size fields correctly. 5) Remove unnecessary usize->u32 casts by making the appropriate fields usize. 6) Add header values to request and response header messages. 7) Support triggering page reloads via the devtools client. --- components/devtools/actors/network_event.rs | 140 +++++++++++++------- components/devtools/actors/root.rs | 6 +- components/devtools/actors/tab.rs | 13 +- components/devtools/lib.rs | 126 +++++++----------- components/devtools/protocol.rs | 14 ++ components/devtools_traits/lib.rs | 7 +- components/net/fetch/methods.rs | 2 +- components/net/http_loader.rs | 83 +++++++++--- components/script/devtools.rs | 9 ++ components/script/script_thread.rs | 2 + tests/unit/net/http_loader.rs | 5 +- 11 files changed, 252 insertions(+), 155 deletions(-) diff --git a/components/devtools/actors/network_event.rs b/components/devtools/actors/network_event.rs index a6282b71918..38f7d5767df 100644 --- a/components/devtools/actors/network_event.rs +++ b/components/devtools/actors/network_event.rs @@ -27,7 +27,10 @@ struct HttpRequest { method: Method, headers: Headers, body: Option>, - startedDateTime: Tm + startedDateTime: Tm, + timeStamp: i64, + connect_time: u64, + send_time: u64, } struct HttpResponse { @@ -48,13 +51,14 @@ pub struct EventActor { pub url: String, pub method: String, pub startedDateTime: String, + pub timeStamp: i64, pub isXHR: bool, pub private: bool } #[derive(Serialize)] pub struct ResponseCookiesMsg { - pub cookies: u32, + pub cookies: usize, } #[derive(Serialize)] @@ -64,7 +68,7 @@ pub struct ResponseStartMsg { pub remotePort: u32, pub status: String, pub statusText: String, - pub headersSize: u32, + pub headersSize: usize, pub discardResponseBody: bool, } @@ -79,29 +83,41 @@ pub struct ResponseContentMsg { #[derive(Serialize)] pub struct ResponseHeadersMsg { - pub headers: u32, - pub headersSize: u32, + pub headers: usize, + pub headersSize: usize, } #[derive(Serialize)] pub struct RequestCookiesMsg { - pub cookies: u32, + pub cookies: usize, +} + +#[derive(Serialize)] +pub struct RequestHeadersMsg { + headers: usize, + headersSize: usize, } #[derive(Serialize)] struct GetRequestHeadersReply { from: String, - headers: Vec, - headerSize: u8, + headers: Vec
, + headerSize: usize, rawHeaders: String } +#[derive(Serialize)] +struct Header { + name: String, + value: String, +} + #[derive(Serialize)] struct GetResponseHeadersReply { from: String, - headers: Vec, - headerSize: u8, + headers: Vec
, + headerSize: usize, rawHeaders: String } @@ -135,8 +151,8 @@ struct GetResponseCookiesReply { struct Timings { blocked: u32, dns: u32, - connect: u32, - send: u32, + connect: u64, + send: u64, wait: u32, receive: u32, } @@ -145,16 +161,20 @@ struct Timings { struct GetEventTimingsReply { from: String, timings: Timings, - totalTime: u32, + totalTime: u64, +} + +#[derive(Serialize)] +struct SecurityInfo { + state: String, } #[derive(Serialize)] struct GetSecurityInfoReply { from: String, - seuritInfo: String, + securityInfo: SecurityInfo, } - impl Actor for NetworkEventActor { fn name(&self) -> String { self.name.clone() @@ -167,19 +187,19 @@ impl Actor for NetworkEventActor { stream: &mut TcpStream) -> Result { Ok(match msg_type { "getRequestHeaders" => { - // TODO: Pass the correct values for headers, headerSize, rawHeaders - let headersSize = self.request.headers.len() as u8; - let mut headerNames = Vec::new(); + let mut headers = Vec::new(); let mut rawHeadersString = "".to_owned(); + let mut headersSize = 0; for item in self.request.headers.iter() { let name = item.name(); let value = item.value_string(); - headerNames.push(name.to_owned()); rawHeadersString = rawHeadersString + name + ":" + &value + "\r\n"; + headersSize += name.len() + value.len(); + headers.push(Header { name: name.to_owned(), value: value.to_owned() }); } let msg = GetRequestHeadersReply { from: self.name(), - headers: headerNames, + headers: headers, headerSize: headersSize, rawHeaders: rawHeadersString, }; @@ -213,25 +233,32 @@ impl Actor for NetworkEventActor { ActorMessageStatus::Processed } "getResponseHeaders" => { - if let Some(ref headers) = self.response.headers { - let headersSize = headers.len() as u8; - let mut headerNames = Vec::new(); + if let Some(ref response_headers) = self.response.headers { + let mut headers = vec![]; let mut rawHeadersString = "".to_owned(); - for item in headers.iter() { + let mut headersSize = 0; + for item in response_headers.iter() { let name = item.name(); let value = item.value_string(); - headerNames.push(name.to_owned()); - rawHeadersString = rawHeadersString + name + ":" + &value + "\r\n"; + headers.push(Header { + name: name.to_owned(), + value: value.clone(), + }); + headersSize += name.len() + value.len(); + rawHeadersString.push_str(name); + rawHeadersString.push_str(":"); + rawHeadersString.push_str(&value); + rawHeadersString.push_str("\r\n"); } let msg = GetResponseHeadersReply { from: self.name(), - headers: headerNames, + headers: headers, headerSize: headersSize, rawHeaders: rawHeadersString, }; stream.write_json_packet(&msg); } - ActorMessageStatus::Processed + ActorMessageStatus::Processed } "getResponseCookies" => { let mut cookies = Vec::new(); @@ -254,7 +281,7 @@ impl Actor for NetworkEventActor { let msg = GetResponseContentReply { from: self.name(), content: self.response.body.clone(), - contentDiscarded: false, + contentDiscarded: self.response.body.is_none(), }; stream.write_json_packet(&msg); ActorMessageStatus::Processed @@ -264,16 +291,17 @@ impl Actor for NetworkEventActor { let timingsObj = Timings { blocked: 0, dns: 0, - connect: 0, - send: 0, + connect: self.request.connect_time, + send: self.request.send_time, wait: 0, receive: 0, }; + let total = timingsObj.connect + timingsObj.send; // TODO: Send the correct values for all these fields. let msg = GetEventTimingsReply { from: self.name(), timings: timingsObj, - totalTime: 0, + totalTime: total, }; stream.write_json_packet(&msg); ActorMessageStatus::Processed @@ -282,7 +310,9 @@ impl Actor for NetworkEventActor { // TODO: Send the correct values for securityInfo. let msg = GetSecurityInfoReply { from: self.name(), - seuritInfo: "".to_owned(), + securityInfo: SecurityInfo { + state: "insecure".to_owned() + }, }; stream.write_json_packet(&msg); ActorMessageStatus::Processed @@ -302,6 +332,9 @@ impl NetworkEventActor { headers: Headers::new(), body: None, startedDateTime: time::now(), + timeStamp: time::get_time().sec, + send_time: 0, + connect_time: 0, }, response: HttpResponse { headers: None, @@ -317,6 +350,9 @@ impl NetworkEventActor { self.request.headers = request.headers.clone(); self.request.body = request.body; self.request.startedDateTime = request.startedDateTime; + self.request.timeStamp = request.timeStamp; + self.request.connect_time = request.connect_time; + self.request.send_time = request.send_time; } pub fn add_response(&mut self, response: DevtoolsHttpResponse) { @@ -332,6 +368,7 @@ impl NetworkEventActor { url: self.request.url.clone(), method: format!("{}", self.request.method), startedDateTime: format!("{}", self.request.startedDateTime.rfc3339()), + timeStamp: self.request.timeStamp, isXHR: false, private: false, } @@ -339,7 +376,7 @@ impl NetworkEventActor { pub fn response_start(&self) -> ResponseStartMsg { // TODO: Send the correct values for all these fields. - let hSizeOption = self.response.headers.as_ref().map(|headers| headers.len() as u32); + let hSizeOption = self.response.headers.as_ref().map(|headers| headers.len()); let hSize = hSizeOption.unwrap_or(0); let (status_code, status_message) = self.response.status.as_ref(). map_or((0, "".to_owned()), |&RawStatus(ref code, ref text)| (*code, text.clone().into_owned())); @@ -368,7 +405,7 @@ impl NetworkEventActor { mimeType: mString, contentSize: 0, transferredSize: 0, - discardResponseBody: false, + discardResponseBody: true, } } @@ -381,7 +418,7 @@ impl NetworkEventActor { }; } ResponseCookiesMsg { - cookies: cookies_size as u32, + cookies: cookies_size, } } @@ -389,7 +426,7 @@ impl NetworkEventActor { let mut headers_size = 0; let mut headers_byte_count = 0; if let Some(ref headers) = self.response.headers { - headers_size = headers.len() as u32; + headers_size = headers.len(); for item in headers.iter() { headers_byte_count += item.name().len() + item.value_string().len(); } @@ -397,21 +434,32 @@ impl NetworkEventActor { } ResponseHeadersMsg { headers: headers_size, - headersSize: headers_byte_count as u32, + headersSize: headers_byte_count, + } + } + + pub fn request_headers(&self) -> RequestHeadersMsg { + let size = self.request + .headers + .iter() + .fold(0, |acc, h| acc + h.name().len() + h.value_string().len()); + RequestHeadersMsg { + headers: self.request.headers.len(), + headersSize: size, } } pub fn request_cookies(&self) -> RequestCookiesMsg { - let mut cookies_size = 0; - if let Some(ref headers) = self.response.headers { - cookies_size = match headers.get() { - Some(&Cookie(ref cookie)) => cookie.len(), - None => 0 - }; - } + let cookies_size = match self.request.headers.get() { + Some(&Cookie(ref cookie)) => cookie.len(), + None => 0 + }; RequestCookiesMsg { - cookies: cookies_size as u32, + cookies: cookies_size, } } + pub fn total_time(&self) -> u64 { + self.request.connect_time + self.request.send_time + } } diff --git a/components/devtools/actors/root.rs b/components/devtools/actors/root.rs index 6103e5a9a4f..1885e775ff6 100644 --- a/components/devtools/actors/root.rs +++ b/components/devtools/actors/root.rs @@ -19,7 +19,8 @@ use std::net::TcpStream; struct ActorTraits { sources: bool, highlightable: bool, - customHighlighters: Vec, + customHighlighters: bool, + networkMonitor: bool, } #[derive(Serialize)] @@ -117,7 +118,8 @@ impl RootActor { traits: ActorTraits { sources: true, highlightable: true, - customHighlighters: vec!("BoxModelHighlighter".to_owned()), + customHighlighters: true, + networkMonitor: true }, } } diff --git a/components/devtools/actors/tab.rs b/components/devtools/actors/tab.rs index 51694c9aa7a..fd93553e6b7 100644 --- a/components/devtools/actors/tab.rs +++ b/components/devtools/actors/tab.rs @@ -9,7 +9,7 @@ use actor::{Actor, ActorMessageStatus, ActorRegistry}; use actors::console::ConsoleActor; -use devtools_traits::DevtoolScriptControlMsg::WantsLiveNotifications; +use devtools_traits::DevtoolScriptControlMsg::{self, WantsLiveNotifications}; use protocol::JsonPacketStream; use serde_json::Value; use std::collections::BTreeMap; @@ -88,10 +88,19 @@ impl Actor for TabActor { fn handle_message(&self, registry: &ActorRegistry, msg_type: &str, - _msg: &BTreeMap, + msg: &BTreeMap, stream: &mut TcpStream) -> Result { Ok(match msg_type { "reconfigure" => { + if let Some(options) = msg.get("options").and_then(|o| o.as_object()) { + if let Some(val) = options.get("performReload") { + if val.as_boolean().unwrap_or(false) { + let console_actor = registry.find::(&self.console); + let _ = console_actor.script_chan.send( + DevtoolScriptControlMsg::Reload(console_actor.pipeline)); + } + } + } stream.write_json_packet(&ReconfigureReply { from: self.name() }); ActorMessageStatus::Processed } diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index a0d514f9025..42f5ce86a02 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -35,8 +35,7 @@ use actor::{Actor, ActorRegistry}; use actors::console::ConsoleActor; use actors::framerate::FramerateActor; use actors::inspector::InspectorActor; -use actors::network_event::{EventActor, NetworkEventActor, RequestCookiesMsg, ResponseCookiesMsg }; -use actors::network_event::{ResponseContentMsg, ResponseHeadersMsg, ResponseStartMsg }; +use actors::network_event::{EventActor, NetworkEventActor, ResponseStartMsg}; use actors::performance::PerformanceActor; use actors::profiler::ProfilerActor; use actors::root::RootActor; @@ -105,6 +104,24 @@ struct NetworkEventMsg { eventActor: EventActor, } +#[derive(Serialize)] +struct NetworkEventUpdateMsg { + from: String, + #[serde(rename = "type")] + type_: String, + updateType: String, +} + +#[derive(Serialize)] +struct EventTimingsUpdateMsg { + totalTime: u64, +} + +#[derive(Serialize)] +struct SecurityInfoUpdateMsg { + state: String, +} + #[derive(Serialize)] struct ResponseStartUpdateMsg { from: String, @@ -114,60 +131,6 @@ struct ResponseStartUpdateMsg { response: ResponseStartMsg, } -#[derive(Serialize)] -struct ResponseContentUpdateMsg { - from: String, - #[serde(rename = "type")] - type_: String, - updateType: String, - responseContent: ResponseContentMsg, -} - -#[derive(Serialize)] -struct ResponseCookiesUpdateMsg { - from: String, - #[serde(rename = "type")] - type_: String, - updateType: String, - responseCookies: ResponseCookiesMsg, -} - -#[derive(Serialize)] -struct ResponseHeadersUpdateMsg { - from: String, - #[serde(rename = "type")] - type_: String, - updateType: String, - responseHeaders: ResponseHeadersMsg, -} - -#[derive(Serialize)] -struct RequestCookiesUpdateMsg { - from: String, - #[serde(rename = "type")] - type_: String, - updateType: String, - requestcookies: RequestCookiesMsg, -} - -#[derive(Serialize)] -struct EventTimingsUpdateMsg { - from: String, - #[serde(rename = "type")] - type_: String, - updateType: String, - totalTime: u32, -} - -#[derive(Serialize)] -struct SecurityInfoUpdateMsg { - from: String, - #[serde(rename = "type")] - type_: String, - updateType: String, - securityState: String, -} - /// Spin up a devtools server that listens for connections on the specified port. pub fn start_server(port: u16) -> Sender { let (sender, receiver) = channel(); @@ -416,14 +379,22 @@ fn run_server(sender: Sender, //Store the response information in the actor actor.add_response(httpresponse); - let msg7 = RequestCookiesUpdateMsg { + let msg = NetworkEventUpdateMsg { + from: netevent_actor_name.clone(), + type_: "networkEventUpdate".to_owned(), + updateType: "requestHeaders".to_owned(), + }; + for stream in &mut connections { + stream.write_merged_json_packet(&msg, &actor.request_headers()); + } + + let msg = NetworkEventUpdateMsg { from: netevent_actor_name.clone(), type_: "networkEventUpdate".to_owned(), updateType: "requestCookies".to_owned(), - requestcookies: actor.request_cookies(), }; for stream in &mut connections { - stream.write_json_packet(&msg7); + stream.write_merged_json_packet(&msg, &actor.request_cookies()); } //Send a networkEventUpdate (responseStart) to the client @@ -437,61 +408,56 @@ fn run_server(sender: Sender, for stream in &mut connections { stream.write_json_packet(&msg); } - let msg2 = EventTimingsUpdateMsg { + let msg = NetworkEventUpdateMsg { from: netevent_actor_name.clone(), type_: "networkEventUpdate".to_owned(), updateType: "eventTimings".to_owned(), - totalTime: 0 }; - + let extra = EventTimingsUpdateMsg { + totalTime: actor.total_time(), + }; for stream in &mut connections { - stream.write_json_packet(&msg2); + stream.write_merged_json_packet(&msg, &extra); } - let msg3 = SecurityInfoUpdateMsg { + let msg = NetworkEventUpdateMsg { from: netevent_actor_name.clone(), type_: "networkEventUpdate".to_owned(), updateType: "securityInfo".to_owned(), - securityState: "".to_owned(), }; - + let extra = SecurityInfoUpdateMsg { + state: "insecure".to_owned(), + }; for stream in &mut connections { - stream.write_json_packet(&msg3); + stream.write_merged_json_packet(&msg, &extra); } - let msg4 = ResponseContentUpdateMsg { + let msg = NetworkEventUpdateMsg { from: netevent_actor_name.clone(), type_: "networkEventUpdate".to_owned(), updateType: "responseContent".to_owned(), - responseContent: actor.response_content(), }; - for stream in &mut connections { - stream.write_json_packet(&msg4); + stream.write_merged_json_packet(&msg, &actor.response_content()); } - let msg5 = ResponseCookiesUpdateMsg { + let msg = NetworkEventUpdateMsg { from: netevent_actor_name.clone(), type_: "networkEventUpdate".to_owned(), updateType: "responseCookies".to_owned(), - responseCookies: actor.response_cookies(), }; - for stream in &mut connections { - stream.write_json_packet(&msg5); + stream.write_merged_json_packet(&msg, &actor.response_cookies()); } - let msg6 = ResponseHeadersUpdateMsg { + let msg = NetworkEventUpdateMsg { from: netevent_actor_name.clone(), type_: "networkEventUpdate".to_owned(), updateType: "responseHeaders".to_owned(), - responseHeaders: actor.response_headers(), }; - for stream in &mut connections { - stream.write_json_packet(&msg6); + stream.write_merged_json_packet(&msg, &actor.response_headers()); } - } } } diff --git a/components/devtools/protocol.rs b/components/devtools/protocol.rs index 3da44cc5323..b385f396c1b 100644 --- a/components/devtools/protocol.rs +++ b/components/devtools/protocol.rs @@ -28,6 +28,7 @@ pub struct Method { pub trait JsonPacketStream { fn write_json_packet(&mut self, obj: &T); + fn write_merged_json_packet(&mut self, base: &T, extra: &U); fn read_json_packet(&mut self) -> Result, String>; } @@ -38,6 +39,19 @@ impl JsonPacketStream for TcpStream { write!(self, "{}:{}", s.len(), s).unwrap(); } + fn write_merged_json_packet(&mut self, base: &T, extra: &U) { + let mut obj = serde_json::to_value(base); + let obj = obj.as_object_mut().unwrap(); + let extra = serde_json::to_value(extra); + let extra = extra.as_object().unwrap(); + + for (key, value) in extra { + obj.insert(key.to_owned(), value.to_owned()); + } + + self.write_json_packet(obj); + } + fn read_json_packet(&mut self) -> Result, String> { // https://wiki.mozilla.org/Remote_Debugging_Protocol_Stream_Transport // In short, each JSON packet is [ascii length]:[JSON data of given length] diff --git a/components/devtools_traits/lib.rs b/components/devtools_traits/lib.rs index 381af6016b3..d64ac04b16f 100644 --- a/components/devtools_traits/lib.rs +++ b/components/devtools_traits/lib.rs @@ -214,6 +214,8 @@ pub enum DevtoolScriptControlMsg { /// Request a callback directed at the given actor name from the next animation frame /// executed in the given pipeline. RequestAnimationFrame(PipelineId, String), + /// Direct the given pipeline to reload the current page. + Reload(PipelineId), } #[derive(Deserialize, Serialize)] @@ -292,7 +294,10 @@ pub struct HttpRequest { pub headers: Headers, pub body: Option>, pub pipeline_id: PipelineId, - pub startedDateTime: Tm + pub startedDateTime: Tm, + pub timeStamp: i64, + pub connect_time: u64, + pub send_time: u64, } #[derive(Debug, PartialEq)] diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 45ecbd1e603..16d64a86a7c 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -816,7 +816,7 @@ fn http_network_fetch(request: Rc, let mut response = Response::new(); match wrapped_response { - Ok(mut res) => { + Ok((mut res, _)) => { response.url = Some(res.response.url.clone()); response.status = Some(res.response.status); response.headers = res.response.headers.clone(); diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index a6b810cc18a..f9fc6224d36 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -119,6 +119,10 @@ impl HttpState { } } +fn precise_time_ms() -> u64 { + time::precise_time_ns() / (1000 * 1000) +} + fn load_for_consumer(load_data: LoadData, start_chan: LoadConsumer, classifier: Arc, @@ -552,21 +556,34 @@ enum Decoder { Plain(Box) } -fn send_request_to_devtools(devtools_chan: Option>, - request_id: String, +fn prepare_devtools_request(request_id: String, url: Url, method: Method, headers: Headers, body: Option>, - pipeline_id: PipelineId, now: Tm) { - if let Some(ref chan) = devtools_chan { - let request = DevtoolsHttpRequest { - url: url, method: method, headers: headers, body: body, pipeline_id: pipeline_id, startedDateTime: now }; - let net_event = NetworkEvent::HttpRequest(request); + pipeline_id: PipelineId, + now: Tm, + connect_time: u64, + send_time: u64) -> ChromeToDevtoolsControlMsg { + let request = DevtoolsHttpRequest { + url: url, + method: method, + headers: headers, + body: body, + pipeline_id: pipeline_id, + startedDateTime: now, + timeStamp: now.to_timespec().sec, + connect_time: connect_time, + send_time: send_time, + }; + let net_event = NetworkEvent::HttpRequest(request); - let msg = ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event); - chan.send(DevtoolsControlMsg::FromChrome(msg)).unwrap(); - } + ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event) +} + +fn send_request_to_devtools(msg: ChromeToDevtoolsControlMsg, + devtools_chan: &Sender) { + devtools_chan.send(DevtoolsControlMsg::FromChrome(msg)).unwrap(); } fn send_response_to_devtools(devtools_chan: Option>, @@ -701,10 +718,12 @@ pub fn obtain_response(request_factory: &HttpRequestFactory, iters: u32, devtools_chan: &Option>, request_id: &str) - -> Result where A: HttpRequest + 'static { + -> Result<(A::R, Option), LoadError> + where A: HttpRequest + 'static { let null_data = None; let response; let connection_url = replace_hosts(&url); + let mut msg; // loop trying connections in connection pool // they may have grown stale (disconnected), in which case we'll get @@ -742,22 +761,36 @@ pub fn obtain_response(request_factory: &HttpRequestFactory, info!("{:?}", data); } + let connect_start = precise_time_ms(); + let req = try!(request_factory.create(connection_url.clone(), method.clone(), headers.clone())); + let connect_end = precise_time_ms(); + if cancel_listener.is_cancelled() { return Err(LoadError::new(connection_url.clone(), LoadErrorType::Cancelled)); } + let send_start = precise_time_ms(); + let maybe_response = req.send(request_body); - if let Some(pipeline_id) = *pipeline_id { - send_request_to_devtools( - devtools_chan.clone(), request_id.clone().into(), - url.clone(), method.clone(), headers, - request_body.clone(), pipeline_id, time::now() - ); - } + let send_end = precise_time_ms(); + + msg = if devtools_chan.is_some() { + if let Some(pipeline_id) = *pipeline_id { + Some(prepare_devtools_request( + request_id.clone().into(), + url.clone(), method.clone(), headers, + request_body.clone(), pipeline_id, time::now(), + connect_end - connect_start, send_end - send_start)) + } else { + None + } + } else { + None + }; response = match maybe_response { Ok(r) => r, @@ -775,7 +808,7 @@ pub fn obtain_response(request_factory: &HttpRequestFactory, break; } - Ok(response) + Ok((response, msg)) } pub trait UIProvider { @@ -914,9 +947,10 @@ pub fn load(load_data: &LoadData, request_headers.set(auth_header.clone()); } - let response = try!(obtain_response(request_factory, &doc_url, &method, &request_headers, - &cancel_listener, &load_data.data, &load_data.method, - &load_data.pipeline_id, iters, &devtools_chan, &request_id)); + let (response, msg) = + try!(obtain_response(request_factory, &doc_url, &method, &request_headers, + &cancel_listener, &load_data.data, &load_data.method, + &load_data.pipeline_id, iters, &devtools_chan, &request_id)); process_response_headers(&response, &doc_url, &http_state.cookie_jar, &http_state.hsts_list, &load_data); @@ -1006,6 +1040,11 @@ pub fn load(load_data: &LoadData, HttpsState::None }; + // Only notify the devtools about the final request that received a response. + if let Some(msg) = msg { + send_request_to_devtools(msg, devtools_chan.as_ref().unwrap()); + } + // --- Tell devtools that we got a response // Send an HttpResponse message to devtools with the corresponding request_id // TODO: Send this message even when the load fails? diff --git a/components/script/devtools.rs b/components/script/devtools.rs index 8f783126a62..7533b159771 100644 --- a/components/script/devtools.rs +++ b/components/script/devtools.rs @@ -10,6 +10,7 @@ use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclar use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; +use dom::bindings::codegen::Bindings::LocationBinding::LocationMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::conversions::{FromJSValConvertible, jsstring_to_str}; use dom::bindings::global::GlobalRef; @@ -250,3 +251,11 @@ pub fn handle_request_animation_frame(context: &BrowsingContext, devtools_sender.send(msg).unwrap(); }); } + +pub fn handle_reload(context: &BrowsingContext, + id: PipelineId) { + let context = context.find(id).expect("There is no such context"); + let win = context.active_window(); + let location = win.Location(); + location.Reload(); +} diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 35ae783f5ff..22c46840445 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -1021,6 +1021,8 @@ impl ScriptThread { devtools::handle_drop_timeline_markers(&context, marker_types), DevtoolScriptControlMsg::RequestAnimationFrame(pipeline_id, name) => devtools::handle_request_animation_frame(&context, pipeline_id, name), + DevtoolScriptControlMsg::Reload(pipeline_id) => + devtools::handle_reload(&context, pipeline_id), } } diff --git a/tests/unit/net/http_loader.rs b/tests/unit/net/http_loader.rs index ba1a7d6f2b0..e414f2f39f2 100644 --- a/tests/unit/net/http_loader.rs +++ b/tests/unit/net/http_loader.rs @@ -503,7 +503,10 @@ fn test_request_and_response_data_with_network_messages() { headers: headers, body: None, pipeline_id: pipeline_id, - startedDateTime: devhttprequest.startedDateTime + startedDateTime: devhttprequest.startedDateTime, + timeStamp: devhttprequest.timeStamp, + connect_time: devhttprequest.connect_time, + send_time: devhttprequest.send_time, }; let content = "Yay!";