bors-servo 2015-02-04 11:57:54 -07:00
commit 7e3f504d94
26 changed files with 6627 additions and 154 deletions

View file

@ -9,3 +9,6 @@ path = "lib.rs"
[dependencies.msg] [dependencies.msg]
path = "../msg" path = "../msg"
[dependencies.util]
path = "../util"

View file

@ -22,7 +22,7 @@ git = "https://github.com/servo/rust-geom"
[dependencies.hyper] [dependencies.hyper]
git = "https://github.com/servo/hyper" git = "https://github.com/servo/hyper"
branch = "servo" branch = "old_servo_new_cookies"
[dependencies.layers] [dependencies.layers]
git = "https://github.com/servo/rust-layers" git = "https://github.com/servo/rust-layers"
@ -34,4 +34,4 @@ git = "https://github.com/servo/rust-core-foundation"
git = "https://github.com/servo/rust-io-surface" git = "https://github.com/servo/rust-io-surface"
[dependencies] [dependencies]
url = "0.2.16" url = "0.2.16"

View file

@ -15,7 +15,11 @@ git = "https://github.com/servo/rust-geom"
[dependencies.hyper] [dependencies.hyper]
git = "https://github.com/servo/hyper" git = "https://github.com/servo/hyper"
branch = "servo" branch = "old_servo_new_cookies"
[dependencies.cookie]
git = "https://github.com/servo/cookie-rs"
branch = "lenientparse_backport"
[dependencies.png] [dependencies.png]
git = "https://github.com/servo/rust-png" git = "https://github.com/servo/rust-png"
@ -26,4 +30,4 @@ git = "https://github.com/servo/rust-stb-image"
[dependencies] [dependencies]
url = "0.2.16" url = "0.2.16"
time = "0.1.12" time = "0.1.12"
openssl="0.2.15" openssl="0.2.15"

View file

@ -26,7 +26,7 @@ pub fn factory(mut load_data: LoadData, start_chan: Sender<TargetedLoadResponse>
content_type: Some(("text".to_string(), "html".to_string())), content_type: Some(("text".to_string(), "html".to_string())),
charset: Some("utf-8".to_string()), charset: Some("utf-8".to_string()),
headers: None, headers: None,
status: Some(RawStatus(200, "OK".to_owned())) status: Some(RawStatus(200, "OK".to_owned())),
}); });
chan.send(Done(Ok(()))).unwrap(); chan.send(Done(Ok(()))).unwrap();
return return

233
components/net/cookie.rs Normal file
View file

@ -0,0 +1,233 @@
/* 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/. */
//! Implementation of cookie creation and matching as specified by
//! http://tools.ietf.org/html/rfc6265
use cookie_storage::CookieSource;
use pub_domains::PUB_DOMAINS;
use cookie_rs;
use time::{Tm, now, at, Timespec};
use url::Url;
use std::borrow::ToOwned;
use std::i64;
use std::io::net::ip::IpAddr;
use std::time::Duration;
/// A stored cookie that wraps the definition in cookie-rs. This is used to implement
/// various behaviours defined in the spec that rely on an associated request URL,
/// which cookie-rs and hyper's header parsing do not support.
#[derive(Clone, Show)]
pub struct Cookie {
pub cookie: cookie_rs::Cookie,
pub host_only: bool,
pub persistent: bool,
pub creation_time: Tm,
pub last_access: Tm,
pub expiry_time: Tm,
}
impl Cookie {
/// http://tools.ietf.org/html/rfc6265#section-5.3
pub fn new_wrapped(mut cookie: cookie_rs::Cookie, request: &Url, source: CookieSource)
-> Option<Cookie> {
// Step 3
let (persistent, expiry_time) = match (&cookie.max_age, &cookie.expires) {
(&Some(max_age), _) => (true, at(now().to_timespec() + Duration::seconds(max_age as i64))),
(_, &Some(expires)) => (true, expires),
_ => (false, at(Timespec::new(i64::MAX, 0)))
};
let url_host = request.host().map(|host| host.serialize()).unwrap_or("".to_owned());
// Step 4
let mut domain = cookie.domain.clone().unwrap_or("".to_owned());
// Step 5
match PUB_DOMAINS.iter().find(|&x| domain == *x) {
Some(val) if *val == url_host => domain = "".to_string(),
Some(_) => return None,
None => {}
}
// Step 6
let host_only = if !domain.is_empty() {
if !Cookie::domain_match(url_host.as_slice(), domain.as_slice()) {
return None;
} else {
cookie.domain = Some(domain);
false
}
} else {
cookie.domain = Some(url_host);
true
};
// Step 7
let mut path = cookie.path.unwrap_or("".to_owned());
if path.is_empty() || path.char_at(0) != '/' {
let url_path = request.serialize_path();
let url_path = url_path.as_ref().map(|path| path.as_slice());
path = Cookie::default_path(url_path.unwrap_or(""));
}
cookie.path = Some(path);
// Step 10
if cookie.httponly && source != CookieSource::HTTP {
return None;
}
Some(Cookie {
cookie: cookie,
host_only: host_only,
persistent: persistent,
creation_time: now(),
last_access: now(),
expiry_time: expiry_time,
})
}
pub fn touch(&mut self) {
self.last_access = now();
}
// http://tools.ietf.org/html/rfc6265#section-5.1.4
fn default_path(request_path: &str) -> String {
if request_path == "" || request_path.char_at(0) != '/' ||
request_path.chars().filter(|&c| c == '/').count() == 1 {
"/".to_owned()
} else if request_path.ends_with("/") {
request_path.slice_to(request_path.len()-1).to_owned()
} else {
request_path.to_owned()
}
}
// http://tools.ietf.org/html/rfc6265#section-5.1.4
pub fn path_match(request_path: &str, cookie_path: &str) -> bool {
request_path == cookie_path ||
( request_path.starts_with(cookie_path) &&
( request_path.ends_with("/") || request_path.char_at(cookie_path.len() - 1) == '/' )
)
}
// http://tools.ietf.org/html/rfc6265#section-5.1.3
pub fn domain_match(string: &str, domain_string: &str) -> bool {
if string == domain_string {
return true;
}
if string.ends_with(domain_string)
&& string.char_at(string.len()-domain_string.len()-1) == '.'
&& string.parse::<IpAddr>().is_none() {
return true;
}
false
}
// http://tools.ietf.org/html/rfc6265#section-5.4 step 1
pub fn appropriate_for_url(&self, url: &Url, source: CookieSource) -> bool {
let domain = url.host().map(|host| host.serialize());
if self.host_only {
if self.cookie.domain != domain {
return false;
}
} else {
if let (Some(ref domain), &Some(ref cookie_domain)) = (domain, &self.cookie.domain) {
if !Cookie::domain_match(domain.as_slice(), cookie_domain.as_slice()) {
return false;
}
}
}
if let (Some(ref path), &Some(ref cookie_path)) = (url.serialize_path(), &self.cookie.path) {
if !Cookie::path_match(path.as_slice(), cookie_path.as_slice()) {
return false;
}
}
if self.cookie.secure && url.scheme != "https".to_string() {
return false;
}
if self.cookie.httponly && source == CookieSource::NonHTTP {
return false;
}
return true;
}
}
#[test]
fn test_domain_match() {
assert!(Cookie::domain_match("foo.com", "foo.com"));
assert!(Cookie::domain_match("bar.foo.com", "foo.com"));
assert!(Cookie::domain_match("baz.bar.foo.com", "foo.com"));
assert!(!Cookie::domain_match("bar.foo.com", "bar.com"));
assert!(!Cookie::domain_match("bar.com", "baz.bar.com"));
assert!(!Cookie::domain_match("foo.com", "bar.com"));
assert!(!Cookie::domain_match("bar.com", "bbar.com"));
assert!(Cookie::domain_match("235.132.2.3", "235.132.2.3"));
assert!(!Cookie::domain_match("235.132.2.3", "1.1.1.1"));
assert!(!Cookie::domain_match("235.132.2.3", ".2.3"));
}
#[test]
fn test_default_path() {
assert!(Cookie::default_path("/foo/bar/baz/").as_slice() == "/foo/bar/baz");
assert!(Cookie::default_path("/foo/").as_slice() == "/foo");
assert!(Cookie::default_path("/foo").as_slice() == "/");
assert!(Cookie::default_path("/").as_slice() == "/");
assert!(Cookie::default_path("").as_slice() == "/");
assert!(Cookie::default_path("foo").as_slice() == "/");
}
#[test]
fn fn_cookie_constructor() {
use cookie_storage::CookieSource;
let url = &Url::parse("http://example.com/foo").unwrap();
let gov_url = &Url::parse("http://gov.ac/foo").unwrap();
// cookie name/value test
assert!(cookie_rs::Cookie::parse(" baz ").is_err());
assert!(cookie_rs::Cookie::parse(" = bar ").is_err());
assert!(cookie_rs::Cookie::parse(" baz = ").is_ok());
// cookie domains test
let cookie = cookie_rs::Cookie::parse(" baz = bar; Domain = ").unwrap();
assert!(Cookie::new_wrapped(cookie.clone(), url, CookieSource::HTTP).is_some());
let cookie = Cookie::new_wrapped(cookie, url, CookieSource::HTTP).unwrap();
assert!(cookie.cookie.domain.as_ref().unwrap().as_slice() == "example.com");
// cookie public domains test
let cookie = cookie_rs::Cookie::parse(" baz = bar; Domain = gov.ac").unwrap();
assert!(Cookie::new_wrapped(cookie.clone(), url, CookieSource::HTTP).is_none());
assert!(Cookie::new_wrapped(cookie, gov_url, CookieSource::HTTP).is_some());
// cookie domain matching test
let cookie = cookie_rs::Cookie::parse(" baz = bar ; Secure; Domain = bazample.com").unwrap();
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_none());
let cookie = cookie_rs::Cookie::parse(" baz = bar ; Secure; Path = /foo/bar/").unwrap();
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_some());
let cookie = cookie_rs::Cookie::parse(" baz = bar ; HttpOnly").unwrap();
assert!(Cookie::new_wrapped(cookie, url, CookieSource::NonHTTP).is_none());
let cookie = cookie_rs::Cookie::parse(" baz = bar ; Secure; Path = /foo/bar/").unwrap();
let cookie = Cookie::new_wrapped(cookie, url, CookieSource::HTTP).unwrap();
assert!(cookie.cookie.value.as_slice() == "bar");
assert!(cookie.cookie.name.as_slice() == "baz");
assert!(cookie.cookie.secure);
assert!(cookie.cookie.path.as_ref().unwrap().as_slice() == "/foo/bar/");
assert!(cookie.cookie.domain.as_ref().unwrap().as_slice() == "example.com");
assert!(cookie.host_only);
let u = &Url::parse("http://example.com/foobar").unwrap();
let cookie = cookie_rs::Cookie::parse("foobar=value;path=/").unwrap();
assert!(Cookie::new_wrapped(cookie, u, CookieSource::HTTP).is_some());
}

View file

@ -0,0 +1,143 @@
/* 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/. */
//! Implementation of cookie storage as specified in
//! http://tools.ietf.org/html/rfc6265
use url::Url;
use cookie::Cookie;
use std::cmp::Ordering;
/// The creator of a given cookie
#[derive(PartialEq, Copy)]
pub enum CookieSource {
/// An HTTP API
HTTP,
/// A non-HTTP API
NonHTTP,
}
pub struct CookieStorage {
cookies: Vec<Cookie>
}
impl CookieStorage {
pub fn new() -> CookieStorage {
CookieStorage {
cookies: Vec::new()
}
}
// http://tools.ietf.org/html/rfc6265#section-5.3
pub fn remove(&mut self, cookie: &Cookie, source: CookieSource) -> Result<Option<Cookie>, ()> {
// Step 1
let position = self.cookies.iter().position(|c| {
c.cookie.domain == cookie.cookie.domain &&
c.cookie.path == cookie.cookie.path &&
c.cookie.name == cookie.cookie.name
});
if let Some(ind) = position {
let c = self.cookies.remove(ind);
// http://tools.ietf.org/html/rfc6265#section-5.3 step 11.2
if !c.cookie.httponly || source == CookieSource::HTTP {
Ok(Some(c))
} else {
// Undo the removal.
self.cookies.push(c);
Err(())
}
} else {
Ok(None)
}
}
// http://tools.ietf.org/html/rfc6265#section-5.3
pub fn push(&mut self, mut cookie: Cookie, source: CookieSource) {
let old_cookie = self.remove(&cookie, source);
if old_cookie.is_err() {
// This new cookie is not allowed to overwrite an existing one.
return;
}
if cookie.cookie.value.is_empty() {
return;
}
// Step 11
if let Some(old_cookie) = old_cookie.unwrap() {
// Step 11.3
cookie.creation_time = old_cookie.creation_time;
}
// Step 12
self.cookies.push(cookie);
}
fn cookie_comparator(a: &Cookie, b: &Cookie) -> Ordering {
let a_path_len = a.cookie.path.as_ref().map(|p| p.len()).unwrap_or(0);
let b_path_len = b.cookie.path.as_ref().map(|p| p.len()).unwrap_or(0);
match a_path_len.cmp(&b_path_len) {
Ordering::Equal => {
let a_creation_time = a.creation_time.to_timespec();
let b_creation_time = b.creation_time.to_timespec();
a_creation_time.cmp(&b_creation_time)
}
// Ensure that longer paths are sorted earlier than shorter paths
Ordering::Greater => Ordering::Less,
Ordering::Less => Ordering::Greater,
}
}
// http://tools.ietf.org/html/rfc6265#section-5.4
pub fn cookies_for_url(&mut self, url: &Url, source: CookieSource) -> Option<String> {
let filterer = |&:c: &&mut Cookie| -> bool {
info!(" === SENT COOKIE : {} {} {:?} {:?}", c.cookie.name, c.cookie.value, c.cookie.domain, c.cookie.path);
info!(" === SENT COOKIE RESULT {}", c.appropriate_for_url(url, source));
// Step 1
c.appropriate_for_url(url, source)
};
// Step 2
let mut url_cookies: Vec<&mut Cookie> = self.cookies.iter_mut().filter(filterer).collect();
url_cookies.sort_by(|a, b| CookieStorage::cookie_comparator(*a, *b));
let reducer = |&:acc: String, c: &mut &mut Cookie| -> String {
// Step 3
c.touch();
// Step 4
(match acc.len() {
0 => acc,
_ => acc + ";"
}) + c.cookie.name.as_slice() + "=" + c.cookie.value.as_slice()
};
let result = url_cookies.iter_mut().fold("".to_string(), reducer);
info!(" === COOKIES SENT: {}", result);
match result.len() {
0 => None,
_ => Some(result)
}
}
}
#[test]
fn test_sort_order() {
use cookie_rs;
let url = &Url::parse("http://example.com/foo").unwrap();
let a_wrapped = cookie_rs::Cookie::parse("baz=bar; Path=/foo/bar/").unwrap();
let a = Cookie::new_wrapped(a_wrapped.clone(), url, CookieSource::HTTP).unwrap();
let a_prime = Cookie::new_wrapped(a_wrapped, url, CookieSource::HTTP).unwrap();
let b = cookie_rs::Cookie::parse("baz=bar;Path=/foo/bar/baz/").unwrap();
let b = Cookie::new_wrapped(b, url, CookieSource::HTTP).unwrap();
assert!(b.cookie.path.as_ref().unwrap().len() > a.cookie.path.as_ref().unwrap().len());
assert!(CookieStorage::cookie_comparator(&a, &b) == Ordering::Greater);
assert!(CookieStorage::cookie_comparator(&b, &a) == Ordering::Less);
assert!(CookieStorage::cookie_comparator(&a, &a_prime) == Ordering::Less);
assert!(CookieStorage::cookie_comparator(&a_prime, &a) == Ordering::Greater);
assert!(CookieStorage::cookie_comparator(&a, &a) == Ordering::Equal);
}

View file

@ -2,7 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cookie_storage::CookieSource;
use resource_task::{Metadata, TargetedLoadResponse, LoadData, start_sending_opt, ResponseSenders}; use resource_task::{Metadata, TargetedLoadResponse, LoadData, start_sending_opt, ResponseSenders};
use resource_task::ControlMsg;
use resource_task::ProgressMsg::{Payload, Done}; use resource_task::ProgressMsg::{Payload, Done};
use log; use log;
@ -13,19 +15,23 @@ use hyper::header::common::{ContentLength, ContentType, Host, Location};
use hyper::HttpError; use hyper::HttpError;
use hyper::method::Method; use hyper::method::Method;
use hyper::net::HttpConnector; use hyper::net::HttpConnector;
use hyper::status::StatusClass; use hyper::status::{StatusCode, StatusClass};
use std::error::Error; use std::error::Error;
use openssl::ssl::{SslContext, SslVerifyMode}; use openssl::ssl::{SslContext, SslVerifyMode};
use std::io::{IoError, IoErrorKind, Reader}; use std::io::{IoError, IoErrorKind, Reader};
use std::sync::mpsc::Sender; use std::sync::mpsc::{Sender, channel};
use std::thunk::Invoke;
use util::task::spawn_named; use util::task::spawn_named;
use util::resource_files::resources_dir_path; use util::resource_files::resources_dir_path;
use url::{Url, UrlParser}; use url::{Url, UrlParser};
use std::borrow::ToOwned; use std::borrow::ToOwned;
pub fn factory(load_data: LoadData, start_chan: Sender<TargetedLoadResponse>) { pub fn factory(cookies_chan: Sender<ControlMsg>)
spawn_named("http_loader".to_owned(), move || load(load_data, start_chan)) -> Box<Invoke<(LoadData, Sender<TargetedLoadResponse>)> + Send> {
box move |:(load_data, start_chan)| {
spawn_named("http_loader".to_owned(), move || load(load_data, start_chan, cookies_chan))
}
} }
fn send_error(url: Url, err: String, senders: ResponseSenders) { fn send_error(url: Url, err: String, senders: ResponseSenders) {
@ -38,7 +44,7 @@ fn send_error(url: Url, err: String, senders: ResponseSenders) {
}; };
} }
fn load(load_data: LoadData, start_chan: Sender<TargetedLoadResponse>) { fn load(mut load_data: LoadData, start_chan: Sender<TargetedLoadResponse>, cookies_chan: Sender<ControlMsg>) {
// FIXME: At the time of writing this FIXME, servo didn't have any central // FIXME: At the time of writing this FIXME, servo didn't have any central
// location for configuration. If you're reading this and such a // location for configuration. If you're reading this and such a
// repository DOES exist, please update this constant to use it. // repository DOES exist, please update this constant to use it.
@ -61,13 +67,6 @@ fn load(load_data: LoadData, start_chan: Sender<TargetedLoadResponse>) {
return; return;
} }
if redirected_to.contains(&url) {
send_error(url, "redirect loop".to_string(), senders);
return;
}
redirected_to.insert(url.clone());
match url.scheme.as_slice() { match url.scheme.as_slice() {
"http" | "https" => {} "http" | "https" => {}
_ => { _ => {
@ -111,15 +110,45 @@ reason: \"certificate verify failed\" }]";
// Preserve the `host` header set automatically by Request. // Preserve the `host` header set automatically by Request.
let host = req.headers().get::<Host>().unwrap().clone(); let host = req.headers().get::<Host>().unwrap().clone();
*req.headers_mut() = load_data.headers.clone();
// Avoid automatically preserving request headers when redirects occur.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=401564 and
// https://bugzilla.mozilla.org/show_bug.cgi?id=216828 .
// Only preserve ones which have been explicitly marked as such.
if iters == 1 {
let mut combined_headers = load_data.headers.clone();
combined_headers.extend(load_data.preserved_headers.iter());
*req.headers_mut() = combined_headers;
} else {
*req.headers_mut() = load_data.preserved_headers.clone();
}
req.headers_mut().set(host); req.headers_mut().set(host);
let (tx, rx) = channel();
cookies_chan.send(ControlMsg::GetCookiesForUrl(url.clone(), tx, CookieSource::HTTP));
if let Some(cookie_list) = rx.recv().unwrap() {
let mut v = Vec::new();
v.push(cookie_list.into_bytes());
req.headers_mut().set_raw("Cookie".to_owned(), v);
}
// FIXME(seanmonstar): use AcceptEncoding from Hyper once available // FIXME(seanmonstar): use AcceptEncoding from Hyper once available
//if !req.headers.has::<AcceptEncoding>() { //if !req.headers.has::<AcceptEncoding>() {
// We currently don't support HTTP Compression (FIXME #2587) // We currently don't support HTTP Compression (FIXME #2587)
req.headers_mut().set_raw("Accept-Encoding".to_owned(), vec![b"identity".to_vec()]); req.headers_mut().set_raw("Accept-Encoding".to_owned(), vec![b"identity".to_vec()]);
//} //}
if log_enabled!(log::INFO) {
info!("{}", load_data.method);
for header in req.headers().iter() {
info!(" - {}", header);
}
info!("{:?}", load_data.data);
}
// Avoid automatically sending request body if a redirect has occurred.
let writer = match load_data.data { let writer = match load_data.data {
Some(ref data) => { Some(ref data) if iters == 1 => {
req.headers_mut().set(ContentLength(data.len() as u64)); req.headers_mut().set(ContentLength(data.len() as u64));
let mut writer = match req.start() { let mut writer = match req.start() {
Ok(w) => w, Ok(w) => w,
@ -137,7 +166,7 @@ reason: \"certificate verify failed\" }]";
}; };
writer writer
}, },
None => { _ => {
match load_data.method { match load_data.method {
Method::Get | Method::Head => (), Method::Get | Method::Head => (),
_ => req.headers_mut().set(ContentLength(0)) _ => req.headers_mut().set(ContentLength(0))
@ -167,6 +196,16 @@ reason: \"certificate verify failed\" }]";
} }
} }
if let Some(cookies) = response.headers.get_raw("set-cookie") {
for cookie in cookies.iter() {
if let Ok(cookies) = String::from_utf8(cookie.clone()) {
cookies_chan.send(ControlMsg::SetCookiesForUrl(url.clone(),
cookies,
CookieSource::HTTP));
}
}
}
if response.status.class() == StatusClass::Redirection { if response.status.class() == StatusClass::Redirection {
match response.headers.get::<Location>() { match response.headers.get::<Location>() {
Some(&Location(ref new_url)) => { Some(&Location(ref new_url)) => {
@ -193,6 +232,21 @@ reason: \"certificate verify failed\" }]";
}; };
info!("redirecting to {}", new_url); info!("redirecting to {}", new_url);
url = new_url; url = new_url;
// According to https://tools.ietf.org/html/rfc7231#section-6.4.2,
// historically UAs have rewritten POST->GET on 301 and 302 responses.
if load_data.method == Method::Post &&
(response.status == StatusCode::MovedPermanently ||
response.status == StatusCode::Found) {
load_data.method = Method::Get;
}
if redirected_to.contains(&url) {
send_error(url, "redirect loop".to_string(), senders);
return;
}
redirected_to.insert(url.clone());
continue; continue;
} }
None => () None => ()

View file

@ -568,7 +568,8 @@ mod tests {
Url::parse("file:///fake").unwrap())); Url::parse("file:///fake").unwrap()));
on_load.invoke(chan); on_load.invoke(chan);
} }
resource_task::ControlMsg::Exit => break resource_task::ControlMsg::Exit => break,
_ => {}
} }
} }
}) })
@ -730,6 +731,7 @@ mod tests {
resource_task_exited_chan.send(()); resource_task_exited_chan.send(());
break break
} }
_ => {}
} }
} }
}); });
@ -762,7 +764,6 @@ mod tests {
let (image_bin_sent_chan, image_bin_sent) = channel(); let (image_bin_sent_chan, image_bin_sent) = channel();
let (resource_task_exited_chan, resource_task_exited) = channel(); let (resource_task_exited_chan, resource_task_exited) = channel();
let mock_resource_task = spawn_listener(move |port: Receiver<resource_task::ControlMsg>| { let mock_resource_task = spawn_listener(move |port: Receiver<resource_task::ControlMsg>| {
loop { loop {
match port.recv().unwrap() { match port.recv().unwrap() {
@ -782,6 +783,7 @@ mod tests {
resource_task_exited_chan.send(()); resource_task_exited_chan.send(());
break break
} }
_ => {}
} }
} }
}); });

View file

@ -11,6 +11,7 @@
#![allow(missing_copy_implementations)] #![allow(missing_copy_implementations)]
#![allow(unstable)] #![allow(unstable)]
extern crate "cookie" as cookie_rs;
extern crate collections; extern crate collections;
extern crate geom; extern crate geom;
extern crate hyper; extern crate hyper;
@ -38,8 +39,11 @@ pub mod about_loader;
pub mod file_loader; pub mod file_loader;
pub mod http_loader; pub mod http_loader;
pub mod data_loader; pub mod data_loader;
pub mod cookie;
pub mod cookie_storage;
pub mod image_cache_task; pub mod image_cache_task;
pub mod local_image_cache; pub mod local_image_cache;
pub mod pub_domains;
pub mod resource_task; pub mod resource_task;
pub mod storage_task; pub mod storage_task;
mod sniffer_task; mod sniffer_task;

File diff suppressed because it is too large Load diff

View file

@ -10,11 +10,13 @@ use file_loader;
use http_loader; use http_loader;
use sniffer_task; use sniffer_task;
use sniffer_task::SnifferTask; use sniffer_task::SnifferTask;
use cookie_storage::{CookieStorage, CookieSource};
use cookie;
use util::task::spawn_named; use util::task::spawn_named;
use hyper::header::common::UserAgent; use hyper::header::common::UserAgent;
use hyper::header::Headers; use hyper::header::{Headers, Header, SetCookie};
use hyper::http::RawStatus; use hyper::http::RawStatus;
use hyper::method::Method; use hyper::method::Method;
use hyper::mime::{Mime, Attr}; use hyper::mime::{Mime, Attr};
@ -22,10 +24,15 @@ use url::Url;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::mpsc::{channel, Receiver, Sender};
use std::thunk::Invoke;
pub enum ControlMsg { pub enum ControlMsg {
/// Request the data associated with a particular URL /// Request the data associated with a particular URL
Load(LoadData), Load(LoadData),
/// Store a set of cookies for a given originating URL
SetCookiesForUrl(Url, String, CookieSource),
/// Retrieve the stored cookies for a given URL
GetCookiesForUrl(Url, Sender<Option<String>>, CookieSource),
Exit Exit
} }
@ -33,7 +40,10 @@ pub enum ControlMsg {
pub struct LoadData { pub struct LoadData {
pub url: Url, pub url: Url,
pub method: Method, pub method: Method,
/// Headers that will apply to the initial request only
pub headers: Headers, pub headers: Headers,
/// Headers that will apply to the initial request and any redirects
pub preserved_headers: Headers,
pub data: Option<Vec<u8>>, pub data: Option<Vec<u8>>,
pub cors: Option<ResourceCORSData>, pub cors: Option<ResourceCORSData>,
pub consumer: Sender<LoadResponse>, pub consumer: Sender<LoadResponse>,
@ -45,6 +55,7 @@ impl LoadData {
url: url, url: url,
method: Method::Get, method: Method::Get,
headers: Headers::new(), headers: Headers::new(),
preserved_headers: Headers::new(),
data: None, data: None,
cors: None, cors: None,
consumer: consumer, consumer: consumer,
@ -61,6 +72,7 @@ pub struct ResourceCORSData {
} }
/// Metadata about a loaded resource, such as is obtained from HTTP headers. /// Metadata about a loaded resource, such as is obtained from HTTP headers.
#[deriving(Clone)]
pub struct Metadata { pub struct Metadata {
/// Final URL after redirects. /// Final URL after redirects.
pub final_url: Url, pub final_url: Url,
@ -75,7 +87,7 @@ pub struct Metadata {
pub headers: Option<Headers>, pub headers: Option<Headers>,
/// HTTP Status /// HTTP Status
pub status: Option<RawStatus> pub status: Option<RawStatus>,
} }
impl Metadata { impl Metadata {
@ -87,7 +99,7 @@ impl Metadata {
charset: None, charset: None,
headers: None, headers: None,
// http://fetch.spec.whatwg.org/#concept-response-status-message // http://fetch.spec.whatwg.org/#concept-response-status-message
status: Some(RawStatus(200, "OK".to_owned())) status: Some(RawStatus(200, "OK".to_owned())),
} }
} }
@ -184,8 +196,9 @@ pub type ResourceTask = Sender<ControlMsg>;
pub fn new_resource_task(user_agent: Option<String>) -> ResourceTask { pub fn new_resource_task(user_agent: Option<String>) -> ResourceTask {
let (setup_chan, setup_port) = channel(); let (setup_chan, setup_port) = channel();
let sniffer_task = sniffer_task::new_sniffer_task(); let sniffer_task = sniffer_task::new_sniffer_task();
let setup_chan_clone = setup_chan.clone();
spawn_named("ResourceManager".to_owned(), move || { spawn_named("ResourceManager".to_owned(), move || {
ResourceManager::new(setup_port, user_agent, sniffer_task).start(); ResourceManager::new(setup_port, user_agent, sniffer_task, setup_chan_clone).start();
}); });
setup_chan setup_chan
} }
@ -194,26 +207,44 @@ struct ResourceManager {
from_client: Receiver<ControlMsg>, from_client: Receiver<ControlMsg>,
user_agent: Option<String>, user_agent: Option<String>,
sniffer_task: SnifferTask, sniffer_task: SnifferTask,
cookie_storage: CookieStorage,
resource_task: Sender<ControlMsg>,
} }
impl ResourceManager { impl ResourceManager {
fn new(from_client: Receiver<ControlMsg>, user_agent: Option<String>, sniffer_task: SnifferTask) -> ResourceManager { fn new(from_client: Receiver<ControlMsg>, user_agent: Option<String>, sniffer_task: SnifferTask,
resource_task: Sender<ControlMsg>) -> ResourceManager {
ResourceManager { ResourceManager {
from_client: from_client, from_client: from_client,
user_agent: user_agent, user_agent: user_agent,
sniffer_task: sniffer_task, sniffer_task: sniffer_task,
cookie_storage: CookieStorage::new(),
resource_task: resource_task,
} }
} }
} }
impl ResourceManager { impl ResourceManager {
fn start(&self) { fn start(&mut self) {
loop { loop {
match self.from_client.recv().unwrap() { match self.from_client.recv().unwrap() {
ControlMsg::Load(load_data) => { ControlMsg::Load(load_data) => {
self.load(load_data) self.load(load_data)
} }
ControlMsg::SetCookiesForUrl(request, cookie_list, source) => {
let header = Header::parse_header([cookie_list.into_bytes()].as_slice());
if let Some(SetCookie(cookies)) = header {
for bare_cookie in cookies.into_iter() {
if let Some(cookie) = cookie::Cookie::new_wrapped(bare_cookie, &request, source) {
self.cookie_storage.push(cookie, source);
}
}
}
}
ControlMsg::GetCookiesForUrl(url, consumer, source) => {
consumer.send(self.cookie_storage.cookies_for_url(&url, source));
}
ControlMsg::Exit => { ControlMsg::Exit => {
break break
} }
@ -221,7 +252,7 @@ impl ResourceManager {
} }
} }
fn load(&self, load_data: LoadData) { fn load(&mut self, load_data: LoadData) {
let mut load_data = load_data; let mut load_data = load_data;
self.user_agent.as_ref().map(|ua| load_data.headers.set(UserAgent(ua.clone()))); self.user_agent.as_ref().map(|ua| load_data.headers.set(UserAgent(ua.clone())));
let senders = ResponseSenders { let senders = ResponseSenders {
@ -229,19 +260,28 @@ impl ResourceManager {
eventual_consumer: load_data.consumer.clone(), eventual_consumer: load_data.consumer.clone(),
}; };
debug!("resource_task: loading url: {}", load_data.url.serialize()); fn from_factory(factory: fn(LoadData, Sender<TargetedLoadResponse>))
match load_data.url.scheme.as_slice() { -> Box<Invoke<(LoadData, Sender<TargetedLoadResponse>)> + Send> {
"file" => file_loader::factory(load_data, self.sniffer_task.clone()), box move |&:(load_data, start_chan)| {
"http" | "https" => http_loader::factory(load_data, self.sniffer_task.clone()), factory(load_data, start_chan)
"data" => data_loader::factory(load_data, self.sniffer_task.clone()), }
"about" => about_loader::factory(load_data, self.sniffer_task.clone()), }
let loader = match load_data.url.scheme.as_slice() {
"file" => from_factory(file_loader::factory),
"http" | "https" => http_loader::factory(self.resource_task.clone()),
"data" => from_factory(data_loader::factory),
"about" => from_factory(about_loader::factory),
_ => { _ => {
debug!("resource_task: no loader for scheme {}", load_data.url.scheme); debug!("resource_task: no loader for scheme {}", load_data.url.scheme);
start_sending(senders, Metadata::default(load_data.url)) start_sending(senders, Metadata::default(load_data.url))
.send(ProgressMsg::Done(Err("no loader for scheme".to_string()))).unwrap(); .send(ProgressMsg::Done(Err("no loader for scheme".to_string()))).unwrap();
return return
} }
} };
debug!("resource_task: loading url: {}", load_data.url.serialize());
loader.invoke((load_data, self.sniffer_task.clone()));
} }
} }

View file

@ -50,7 +50,7 @@ git = "https://github.com/servo/html5ever"
[dependencies.hyper] [dependencies.hyper]
git = "https://github.com/servo/hyper" git = "https://github.com/servo/hyper"
branch = "servo" branch = "old_servo_new_cookies"
[dependencies.js] [dependencies.js]
git = "https://github.com/servo/rust-mozjs" git = "https://github.com/servo/rust-mozjs"
@ -67,4 +67,4 @@ git = "https://github.com/servo/string-cache"
[dependencies] [dependencies]
encoding = "0.2" encoding = "0.2"
url = "0.2.16" url = "0.2.16"
time = "0.1.12" time = "0.1.12"

View file

@ -19,7 +19,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLAreaElementDerived, HTMLEmbedElem
use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, HTMLImageElementDerived}; use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, HTMLImageElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived}; use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived};
use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::error::Error::{NotSupported, InvalidCharacter}; use dom::bindings::error::Error::{NotSupported, InvalidCharacter, Security};
use dom::bindings::error::Error::{HierarchyRequest, NamespaceError}; use dom::bindings::error::Error::{HierarchyRequest, NamespaceError};
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable}; use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable};
@ -54,6 +54,8 @@ use dom::range::Range;
use dom::treewalker::TreeWalker; use dom::treewalker::TreeWalker;
use dom::uievent::UIEvent; use dom::uievent::UIEvent;
use dom::window::{Window, WindowHelpers}; use dom::window::{Window, WindowHelpers};
use net::resource_task::ControlMsg::{SetCookiesForUrl, GetCookiesForUrl};
use net::cookie_storage::CookieSource::NonHTTP;
use util::namespace; use util::namespace;
use util::str::{DOMString, split_html_space_chars}; use util::str::{DOMString, split_html_space_chars};
@ -68,6 +70,7 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::cell::{Cell, Ref}; use std::cell::{Cell, Ref};
use std::default::Default; use std::default::Default;
use std::sync::mpsc::channel;
use time; use time;
#[derive(PartialEq)] #[derive(PartialEq)]
@ -170,7 +173,7 @@ pub trait DocumentHelpers<'a> {
fn window(self) -> Temporary<Window>; fn window(self) -> Temporary<Window>;
fn encoding_name(self) -> Ref<'a, DOMString>; fn encoding_name(self) -> Ref<'a, DOMString>;
fn is_html_document(self) -> bool; fn is_html_document(self) -> bool;
fn url(self) -> &'a Url; fn url(self) -> Url;
fn quirks_mode(self) -> QuirksMode; fn quirks_mode(self) -> QuirksMode;
fn set_quirks_mode(self, mode: QuirksMode); fn set_quirks_mode(self, mode: QuirksMode);
fn set_last_modified(self, value: DOMString); fn set_last_modified(self, value: DOMString);
@ -206,8 +209,9 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
self.is_html_document self.is_html_document
} }
fn url(self) -> &'a Url { // http://dom.spec.whatwg.org/#dom-document-url
&self.extended_deref().url fn url(self) -> Url {
self.url.clone()
} }
fn quirks_mode(self) -> QuirksMode { fn quirks_mode(self) -> QuirksMode {
@ -1003,7 +1007,38 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
Temporary::new(self.window) Temporary::new(self.window)
} }
// https://html.spec.whatwg.org/multipage/dom.html#dom-document-cookie
fn GetCookie(self) -> Fallible<DOMString> {
//TODO: return empty string for cookie-averse Document
let url = self.url();
if !is_scheme_host_port_tuple(&url) {
return Err(Security);
}
let window = self.window.root();
let page = window.page();
let (tx, rx) = channel();
let _ = page.resource_task.send(GetCookiesForUrl(url, tx, NonHTTP));
let cookies = rx.recv().unwrap();
Ok(cookies.unwrap_or("".to_owned()))
}
// https://html.spec.whatwg.org/multipage/dom.html#dom-document-cookie
fn SetCookie(self, cookie: DOMString) -> ErrorResult {
//TODO: ignore for cookie-averse Document
let url = self.url();
if !is_scheme_host_port_tuple(&url) {
return Err(Security);
}
let window = self.window.root();
let page = window.page();
let _ = page.resource_task.send(SetCookiesForUrl(url, cookie, NonHTTP));
Ok(())
}
global_event_handlers!(); global_event_handlers!();
event_handler!(readystatechange, GetOnreadystatechange, SetOnreadystatechange); event_handler!(readystatechange, GetOnreadystatechange, SetOnreadystatechange);
} }
fn is_scheme_host_port_tuple(url: &Url) -> bool {
url.host().is_some() && url.port_or_default().is_some()
}

View file

@ -777,7 +777,7 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
let base = doc.r().url(); let base = doc.r().url();
// https://html.spec.whatwg.org/multipage/infrastructure.html#reflect // https://html.spec.whatwg.org/multipage/infrastructure.html#reflect
// XXXManishearth this doesn't handle `javascript:` urls properly // XXXManishearth this doesn't handle `javascript:` urls properly
match UrlParser::new().base_url(base).parse(url.as_slice()) { match UrlParser::new().base_url(&base).parse(url.as_slice()) {
Ok(parsed) => parsed.serialize(), Ok(parsed) => parsed.serialize(),
Err(_) => "".to_owned() Err(_) => "".to_owned()
} }
@ -1174,7 +1174,7 @@ impl<'a> VirtualMethods for JSRef<'a, Element> {
// Modifying the `style` attribute might change style. // Modifying the `style` attribute might change style.
let node: JSRef<Node> = NodeCast::from_ref(*self); let node: JSRef<Node> = NodeCast::from_ref(*self);
let doc = document_from_node(*self).root(); let doc = document_from_node(*self).root();
let base_url = doc.r().url().clone(); let base_url = doc.r().url();
let value = attr.value(); let value = attr.value();
let style = Some(parse_style_attribute(value.as_slice(), &base_url)); let style = Some(parse_style_attribute(value.as_slice(), &base_url));
*self.style_attribute.borrow_mut() = style; *self.style_attribute.borrow_mut() = style;

View file

@ -23,6 +23,8 @@ use dom::htmlbuttonelement::{HTMLButtonElement};
use dom::htmltextareaelement::{HTMLTextAreaElement, HTMLTextAreaElementHelpers}; use dom::htmltextareaelement::{HTMLTextAreaElement, HTMLTextAreaElementHelpers};
use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node}; use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node};
use hyper::method::Method; use hyper::method::Method;
use hyper::header::common::ContentType;
use hyper::mime;
use servo_msg::constellation_msg::LoadData; use servo_msg::constellation_msg::LoadData;
use util::str::DOMString; use util::str::DOMString;
use script_task::{ScriptChan, ScriptMsg}; use script_task::{ScriptChan, ScriptMsg};
@ -178,7 +180,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
} }
// TODO: Resolve the url relative to the submitter element // TODO: Resolve the url relative to the submitter element
// Step 10-15 // Step 10-15
let action_components = UrlParser::new().base_url(base).parse(action.as_slice()).unwrap_or(base.clone()); let action_components = UrlParser::new().base_url(&base).parse(action.as_slice()).unwrap_or(base);
let _action = action_components.serialize(); let _action = action_components.serialize();
let scheme = action_components.scheme.clone(); let scheme = action_components.scheme.clone();
let enctype = submitter.enctype(); let enctype = submitter.enctype();
@ -186,12 +188,17 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
let _target = submitter.target(); let _target = submitter.target();
// TODO: Handle browsing contexts, partially loaded documents (step 16-17) // TODO: Handle browsing contexts, partially loaded documents (step 16-17)
let mut load_data = LoadData::new(action_components);
let parsed_data = match enctype { let parsed_data = match enctype {
FormEncType::UrlEncoded => serialize(form_data.iter().map(|d| (d.name.as_slice(), d.value.as_slice()))), FormEncType::UrlEncoded => {
let mime: mime::Mime = "application/x-www-form-urlencoded".parse().unwrap();
load_data.headers.set(ContentType(mime));
serialize(form_data.iter().map(|d| (d.name.as_slice(), d.value.as_slice())))
}
_ => "".to_owned() // TODO: Add serializers for the other encoding types _ => "".to_owned() // TODO: Add serializers for the other encoding types
}; };
let mut load_data = LoadData::new(action_components);
// Step 18 // Step 18
match (scheme.as_slice(), method) { match (scheme.as_slice(), method) {
(_, FormMethod::FormDialog) => return, // Unimplemented (_, FormMethod::FormDialog) => return, // Unimplemented

View file

@ -1574,7 +1574,7 @@ impl Node {
false => IsHTMLDocument::NonHTMLDocument, false => IsHTMLDocument::NonHTMLDocument,
}; };
let window = document.window().root(); let window = document.window().root();
let document = Document::new(window.r(), Some(document.url().clone()), let document = Document::new(window.r(), Some(document.url()),
is_html_doc, None, is_html_doc, None,
DocumentSource::NotFromParser); DocumentSource::NotFromParser);
NodeCast::from_temporary(document) NodeCast::from_temporary(document)

View file

@ -65,6 +65,8 @@ partial interface Document {
readonly attribute DocumentReadyState readyState; readonly attribute DocumentReadyState readyState;
readonly attribute DOMString lastModified; readonly attribute DOMString lastModified;
readonly attribute Location location; readonly attribute Location location;
[Throws]
attribute DOMString cookie;
// DOM tree accessors // DOM tree accessors
[SetterThrows] [SetterThrows]

View file

@ -561,41 +561,35 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
let mut load_data = LoadData::new(self.request_url.borrow().clone().unwrap(), start_chan); let mut load_data = LoadData::new(self.request_url.borrow().clone().unwrap(), start_chan);
load_data.data = extracted; load_data.data = extracted;
// Default headers #[inline]
{ fn join_raw(a: &str, b: &str) -> Vec<u8> {
#[inline] let len = a.len() + b.len();
fn join_raw(a: &str, b: &str) -> Vec<u8> { let mut vec = Vec::with_capacity(len);
let len = a.len() + b.len(); vec.push_all(a.as_bytes());
let mut vec = Vec::with_capacity(len); vec.push_all(b.as_bytes());
vec.push_all(a.as_bytes()); vec
vec.push_all(b.as_bytes()); }
vec
}
let ref mut request_headers = self.request_headers.borrow_mut();
if !request_headers.has::<ContentType>() {
// XHR spec differs from http, and says UTF-8 should be in capitals,
// instead of "utf-8", which is what Hyper defaults to.
let params = ";charset=UTF-8";
let n = "content-type";
match data {
Some(eString(_)) =>
request_headers.set_raw(n.to_owned(), vec![join_raw("text/plain", params)]),
Some(eURLSearchParams(_)) =>
request_headers.set_raw(
n.to_owned(), vec![join_raw("application/x-www-form-urlencoded", params)]),
None => ()
}
}
// XHR spec differs from http, and says UTF-8 should be in capitals,
// instead of "utf-8", which is what Hyper defaults to.
let params = ";charset=UTF-8";
let n = "content-type";
match data {
Some(eString(_)) =>
load_data.headers.set_raw(n.to_owned(), vec![join_raw("text/plain", params)]),
Some(eURLSearchParams(_)) =>
load_data.headers.set_raw(
n.to_owned(), vec![join_raw("application/x-www-form-urlencoded", params)]),
None => ()
}
if !request_headers.has::<Accept>() { load_data.preserved_headers = (*self.request_headers.borrow()).clone();
let mime = Mime(mime::TopLevel::Star, mime::SubLevel::Star, vec![]);
request_headers.set( if !load_data.preserved_headers.has::<Accept>() {
Accept(vec![QualityItem::new(mime, 1.0)])); let mime = Mime(mime::TopLevel::Star, mime::SubLevel::Star, vec![]);
} load_data.preserved_headers.set(Accept(vec![QualityItem::new(mime, 1.0)]));
} // drops the borrow_mut }
load_data.headers = (*self.request_headers.borrow()).clone();
load_data.method = (*self.request_method.borrow()).clone(); load_data.method = (*self.request_method.borrow()).clone();
let (terminate_sender, terminate_receiver) = channel(); let (terminate_sender, terminate_receiver) = channel();
*self.terminate_sender.borrow_mut() = Some(terminate_sender); *self.terminate_sender.borrow_mut() = Some(terminate_sender);
@ -607,8 +601,10 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
} else { } else {
RequestMode::CORS RequestMode::CORS
}; };
let mut combined_headers = load_data.headers.clone();
combined_headers.extend(load_data.preserved_headers.iter());
let cors_request = CORSRequest::maybe_new(referer_url.clone(), load_data.url.clone(), mode, let cors_request = CORSRequest::maybe_new(referer_url.clone(), load_data.url.clone(), mode,
load_data.method.clone(), load_data.headers.clone()); load_data.method.clone(), combined_headers);
match cors_request { match cors_request {
Ok(None) => { Ok(None) => {
let mut buf = String::new(); let mut buf = String::new();

View file

@ -67,7 +67,7 @@ use util::task::spawn_named_with_send_on_failure;
use util::task_state; use util::task_state;
use geom::point::Point2D; use geom::point::Point2D;
use hyper::header::{Header, HeaderFormat}; use hyper::header::{Header, Headers, HeaderFormat};
use hyper::header::shared::util as header_util; use hyper::header::shared::util as header_util;
use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC}; use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC};
use js::jsapi::{JSContext, JSRuntime, JSObject}; use js::jsapi::{JSContext, JSRuntime, JSObject};
@ -787,6 +787,44 @@ impl ScriptTask {
let is_javascript = url.scheme.as_slice() == "javascript"; let is_javascript = url.scheme.as_slice() == "javascript";
self.compositor.borrow_mut().set_ready_state(pipeline_id, Loading);
let (mut parser_input, final_url, last_modified) = if !is_javascript {
// Wait for the LoadResponse so that the parser knows the final URL.
let (input_chan, input_port) = channel();
self.resource_task.send(ControlMsg::Load(NetLoadData {
url: url,
method: load_data.method,
headers: Headers::new(),
preserved_headers: load_data.headers,
data: load_data.data,
cors: None,
consumer: input_chan,
})).unwrap();
let load_response = input_port.recv().unwrap();
let last_modified = load_response.metadata.headers.as_ref().and_then(|headers| {
headers.get().map(|&LastModified(ref tm)| tm.clone())
});
let final_url = load_response.metadata.final_url.clone();
(Some(HTMLInput::InputUrl(load_response)), final_url, last_modified)
} else {
let doc_url = last_url.unwrap_or_else(|| {
Url::parse("about:blank").unwrap()
});
(None, doc_url, None)
};
// Store the final URL before we start parsing, so that DOM routines
// (e.g. HTMLImageElement::update_image) can resolve relative URLs
// correctly.
{
*page.mut_url() = Some((final_url.clone(), true));
}
let cx = self.js_context.borrow(); let cx = self.js_context.borrow();
let cx = cx.as_ref().unwrap(); let cx = cx.as_ref().unwrap();
// Create the window and document objects. // Create the window and document objects.
@ -796,22 +834,15 @@ impl ScriptTask {
self.control_chan.clone(), self.control_chan.clone(),
self.compositor.borrow_mut().dup(), self.compositor.borrow_mut().dup(),
self.image_cache_task.clone()).root(); self.image_cache_task.clone()).root();
let doc_url = if is_javascript {
let doc_url = last_url.unwrap_or_else(|| { let document = Document::new(window.r(), Some(final_url.clone()),
Url::parse("about:blank").unwrap()
});
*page.mut_url() = Some((doc_url.clone(), true));
doc_url
} else {
url.clone()
};
let document = Document::new(window.r(), Some(doc_url.clone()),
IsHTMLDocument::HTMLDocument, None, IsHTMLDocument::HTMLDocument, None,
DocumentSource::FromParser).root(); DocumentSource::FromParser).root();
if let Some(tm) = last_modified {
document.set_last_modified(dom_last_modified(&tm));
}
window.r().init_browser_context(document.r()); window.r().init_browser_context(document.r());
self.compositor.borrow_mut().set_ready_state(pipeline_id, Loading);
{ {
// Create the root frame. // Create the root frame.
@ -822,45 +853,15 @@ impl ScriptTask {
}); });
} }
let (parser_input, final_url) = if !is_javascript { if is_javascript {
// Wait for the LoadResponse so that the parser knows the final URL.
let (input_chan, input_port) = channel();
self.resource_task.send(ControlMsg::Load(NetLoadData {
url: url,
method: load_data.method,
headers: load_data.headers,
data: load_data.data,
cors: None,
consumer: input_chan,
})).unwrap();
let load_response = input_port.recv().unwrap();
load_response.metadata.headers.as_ref().map(|headers| {
headers.get().map(|&LastModified(ref tm)| {
document.r().set_last_modified(dom_last_modified(tm));
});
});
let final_url = load_response.metadata.final_url.clone();
{
// Store the final URL before we start parsing, so that DOM routines
// (e.g. HTMLImageElement::update_image) can resolve relative URLs
// correctly.
*page.mut_url() = Some((final_url.clone(), true));
}
(HTMLInput::InputUrl(load_response), final_url)
} else {
let evalstr = load_data.url.non_relative_scheme_data().unwrap(); let evalstr = load_data.url.non_relative_scheme_data().unwrap();
let jsval = window.r().evaluate_js_on_global_with_result(evalstr); let jsval = window.r().evaluate_js_on_global_with_result(evalstr);
let strval = FromJSValConvertible::from_jsval(self.get_cx(), jsval, let strval = FromJSValConvertible::from_jsval(self.get_cx(), jsval,
StringificationBehavior::Empty); StringificationBehavior::Empty);
(HTMLInput::InputString(strval.unwrap_or("".to_owned())), doc_url) parser_input = Some(HTMLInput::InputString(strval.unwrap_or("".to_owned())));
}; };
parse_html(document.r(), parser_input, &final_url); parse_html(document.r(), parser_input.unwrap(), &final_url);
document.r().set_ready_state(DocumentReadyState::Interactive); document.r().set_ready_state(DocumentReadyState::Interactive);
self.compositor.borrow_mut().set_ready_state(pipeline_id, PerformingLayout); self.compositor.borrow_mut().set_ready_state(pipeline_id, PerformingLayout);

View file

@ -100,7 +100,7 @@ dependencies = [
[[package]] [[package]]
name = "cookie" name = "cookie"
version = "0.1.8" version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/servo/cookie-rs?branch=lenientparse_backport#47ffa4d3c6f85d28f222d6e1d54635fff5622ea3"
dependencies = [ dependencies = [
"openssl 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -154,6 +154,7 @@ name = "devtools_traits"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"msg 0.0.1", "msg 0.0.1",
"util 0.0.1",
] ]
[[package]] [[package]]
@ -398,9 +399,9 @@ source = "git+https://github.com/servo/html5ever#d35dfaaf0d85007057a299afc370d07
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.1.1" version = "0.1.1"
source = "git+https://github.com/servo/hyper?branch=servo#7f48a7e945180a4f762dc75236210d20a69b4a6a" source = "git+https://github.com/servo/hyper?branch=old_servo_new_cookies#7a346f481d683705709526594aa5f13b5c923bc1"
dependencies = [ dependencies = [
"cookie 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "cookie 0.1.8 (git+https://github.com/servo/cookie-rs?branch=lenientparse_backport)",
"log 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mucell 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "mucell 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
@ -541,7 +542,7 @@ dependencies = [
"azure 0.1.0 (git+https://github.com/servo/rust-azure)", "azure 0.1.0 (git+https://github.com/servo/rust-azure)",
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)", "core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=servo)", "hyper 0.1.1 (git+https://github.com/servo/hyper?branch=old_servo_new_cookies)",
"io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)", "io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)",
"layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)",
"style 0.0.1", "style 0.0.1",
@ -558,8 +559,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "net" name = "net"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"cookie 0.1.8 (git+https://github.com/servo/cookie-rs?branch=lenientparse_backport)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=servo)", "hyper 0.1.1 (git+https://github.com/servo/hyper?branch=old_servo_new_cookies)",
"openssl 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"png 0.1.0 (git+https://github.com/servo/rust-png)", "png 0.1.0 (git+https://github.com/servo/rust-png)",
"stb_image 0.1.0 (git+https://github.com/servo/rust-stb-image)", "stb_image 0.1.0 (git+https://github.com/servo/rust-stb-image)",
@ -655,7 +657,7 @@ dependencies = [
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"gfx 0.0.1", "gfx 0.0.1",
"html5ever 0.0.0 (git+https://github.com/servo/html5ever)", "html5ever 0.0.0 (git+https://github.com/servo/html5ever)",
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=servo)", "hyper 0.1.1 (git+https://github.com/servo/hyper?branch=old_servo_new_cookies)",
"js 0.1.0 (git+https://github.com/servo/rust-mozjs)", "js 0.1.0 (git+https://github.com/servo/rust-mozjs)",
"msg 0.0.1", "msg 0.0.1",
"net 0.0.1", "net 0.0.1",

14
ports/cef/Cargo.lock generated
View file

@ -99,7 +99,7 @@ dependencies = [
[[package]] [[package]]
name = "cookie" name = "cookie"
version = "0.1.8" version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/servo/cookie-rs?branch=lenientparse_backport#47ffa4d3c6f85d28f222d6e1d54635fff5622ea3"
dependencies = [ dependencies = [
"openssl 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -153,6 +153,7 @@ name = "devtools_traits"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"msg 0.0.1", "msg 0.0.1",
"util 0.0.1",
] ]
[[package]] [[package]]
@ -397,9 +398,9 @@ source = "git+https://github.com/servo/html5ever#d35dfaaf0d85007057a299afc370d07
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.1.1" version = "0.1.1"
source = "git+https://github.com/servo/hyper?branch=servo#7f48a7e945180a4f762dc75236210d20a69b4a6a" source = "git+https://github.com/servo/hyper?branch=old_servo_new_cookies#7a346f481d683705709526594aa5f13b5c923bc1"
dependencies = [ dependencies = [
"cookie 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "cookie 0.1.8 (git+https://github.com/servo/cookie-rs?branch=lenientparse_backport)",
"log 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mucell 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "mucell 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
@ -540,7 +541,7 @@ dependencies = [
"azure 0.1.0 (git+https://github.com/servo/rust-azure)", "azure 0.1.0 (git+https://github.com/servo/rust-azure)",
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)", "core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=servo)", "hyper 0.1.1 (git+https://github.com/servo/hyper?branch=old_servo_new_cookies)",
"io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)", "io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)",
"layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)",
"style 0.0.1", "style 0.0.1",
@ -557,8 +558,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "net" name = "net"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"cookie 0.1.8 (git+https://github.com/servo/cookie-rs?branch=lenientparse_backport)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=servo)", "hyper 0.1.1 (git+https://github.com/servo/hyper?branch=old_servo_new_cookies)",
"openssl 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"png 0.1.0 (git+https://github.com/servo/rust-png)", "png 0.1.0 (git+https://github.com/servo/rust-png)",
"stb_image 0.1.0 (git+https://github.com/servo/rust-stb-image)", "stb_image 0.1.0 (git+https://github.com/servo/rust-stb-image)",
@ -654,7 +656,7 @@ dependencies = [
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"gfx 0.0.1", "gfx 0.0.1",
"html5ever 0.0.0 (git+https://github.com/servo/html5ever)", "html5ever 0.0.0 (git+https://github.com/servo/html5ever)",
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=servo)", "hyper 0.1.1 (git+https://github.com/servo/hyper?branch=old_servo_new_cookies)",
"js 0.1.0 (git+https://github.com/servo/rust-mozjs)", "js 0.1.0 (git+https://github.com/servo/rust-mozjs)",
"msg 0.0.1", "msg 0.0.1",
"net 0.0.1", "net 0.0.1",

1
ports/gonk/Cargo.lock generated
View file

@ -124,6 +124,7 @@ name = "devtools_traits"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"msg 0.0.1", "msg 0.0.1",
"util 0.0.1",
] ]
[[package]] [[package]]

View file

@ -163,4 +163,3 @@ impl<Window> Browser<Window> where Window: WindowMethods + 'static {
self.compositor.shutdown(); self.compositor.shutdown();
} }
} }

View file

@ -1,5 +0,0 @@
[anonymous-mode-unsupported.htm]
type: testharness
[XMLHttpRequest: anonymous mode unsupported]
expected: FAIL

View file

@ -1,8 +1,5 @@
[document-cookie.html] [document-cookie.html]
type: testharness type: testharness
[document has no cookie]
expected: FAIL
[getting cookie for a cookie-averse document returns empty string, setting does nothing] [getting cookie for a cookie-averse document returns empty string, setting does nothing]
expected: FAIL expected: FAIL

View file

@ -6,9 +6,6 @@
[Document interface: attribute referrer] [Document interface: attribute referrer]
expected: FAIL expected: FAIL
[Document interface: attribute cookie]
expected: FAIL
[Document interface: attribute dir] [Document interface: attribute dir]
expected: FAIL expected: FAIL
@ -1089,9 +1086,6 @@
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "referrer" with the proper type (35)] [Document interface: document.implementation.createDocument(null, "", null) must inherit property "referrer" with the proper type (35)]
expected: FAIL expected: FAIL
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "cookie" with the proper type (36)]
expected: FAIL
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "dir" with the proper type (41)] [Document interface: document.implementation.createDocument(null, "", null) must inherit property "dir" with the proper type (41)]
expected: FAIL expected: FAIL