servo: Merge #8658 - Implement origin concept and browsing contextless documents (from jdm:origin2); r=Ms2ger+jdm

These pave the way for implementing other parts of specifications more thoroughly.

Source-Repo: https://github.com/servo/servo
Source-Revision: 9b57d8d686d361c0dfba1056523cbea12abd148b
This commit is contained in:
Josh Matthews 2016-04-13 15:40:38 +05:01
parent 466c19a211
commit c57148a88d
8 changed files with 233 additions and 20 deletions

View File

@ -89,6 +89,7 @@ use style::properties::PropertyDeclarationBlock;
use style::restyle_hints::ElementSnapshot;
use style::selector_impl::PseudoElement;
use style::values::specified::Length;
use url::Origin as UrlOrigin;
use url::Url;
use util::str::{DOMString, LengthOrPercentageOrAuto};
use uuid::Uuid;
@ -276,7 +277,7 @@ impl<A: JSTraceable, B: JSTraceable, C: JSTraceable> JSTraceable for (A, B, C) {
}
}
no_jsmanaged_fields!(bool, f32, f64, String, Url, AtomicBool, AtomicUsize, Uuid);
no_jsmanaged_fields!(bool, f32, f64, String, Url, AtomicBool, AtomicUsize, UrlOrigin, Uuid);
no_jsmanaged_fields!(usize, u8, u16, u32, u64);
no_jsmanaged_fields!(isize, i8, i16, i32, i64);
no_jsmanaged_fields!(Sender<T>);

View File

@ -94,6 +94,7 @@ use net_traits::CookieSource::NonHTTP;
use net_traits::response::HttpsState;
use net_traits::{AsyncResponseTarget, PendingAsyncLoad};
use num::ToPrimitive;
use origin::Origin;
use script_runtime::ScriptChan;
use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, Runnable};
use script_traits::UntrustedNodeAddress;
@ -223,6 +224,8 @@ pub struct Document {
/// https://html.spec.whatwg.org/multipage/#concept-document-https-state
https_state: Cell<HttpsState>,
touchpad_pressure_phase: Cell<TouchpadPressurePhase>,
/// The document's origin.
origin: Origin,
}
#[derive(JSTraceable, HeapSizeOf)]
@ -1544,14 +1547,6 @@ impl Document {
/// https://html.spec.whatwg.org/multipage/#cookie-averse-document-object
fn is_cookie_averse(&self) -> bool {
/// https://url.spec.whatwg.org/#network-scheme
fn url_has_network_scheme(url: &Url) -> bool {
match &*url.scheme {
"ftp" | "http" | "https" => true,
_ => false,
}
}
self.browsing_context.is_none() || !url_has_network_scheme(&self.url)
}
@ -1590,6 +1585,14 @@ impl LayoutDocumentHelpers for LayoutJS<Document> {
}
}
/// https://url.spec.whatwg.org/#network-scheme
fn url_has_network_scheme(url: &Url) -> bool {
match &*url.scheme {
"ftp" | "http" | "https" => true,
_ => false,
}
}
impl Document {
pub fn new_inherited(window: &Window,
browsing_context: Option<&BrowsingContext>,
@ -1608,6 +1611,15 @@ impl Document {
(DocumentReadyState::Complete, true)
};
// Incomplete implementation of Document origin specification at
// https://html.spec.whatwg.org/multipage/#origin:document
let origin = if url_has_network_scheme(&url) {
Origin::new(&url)
} else {
// Default to DOM standard behaviour
Origin::opaque_identifier()
};
Document {
node: Node::new_document_node(),
window: JS::from_ref(window),
@ -1673,6 +1685,7 @@ impl Document {
css_errors_store: DOMRefCell::new(vec![]),
https_state: Cell::new(HttpsState::None),
touchpad_pressure_phase: Cell::new(TouchpadPressurePhase::BeforeClick),
origin: origin,
}
}
@ -1868,9 +1881,18 @@ impl DocumentMethods for Document {
// https://html.spec.whatwg.org/multipage/#relaxing-the-same-origin-restriction
fn Domain(&self) -> DOMString {
// TODO: This should use the effective script origin when it exists
let origin = self.window.get_url();
DOMString::from(origin.serialize_host().unwrap_or_else(|| "".to_owned()))
// Step 1.
if self.browsing_context().is_none() {
return DOMString::new();
}
if let Some(host) = self.origin.host() {
// Step 4.
DOMString::from(host.serialize())
} else {
// Step 3.
DOMString::new()
}
}
// https://dom.spec.whatwg.org/#dom-document-documenturi
@ -2497,10 +2519,11 @@ impl DocumentMethods for Document {
return Ok(DOMString::new());
}
let url = self.url();
if !is_scheme_host_port_tuple(&url) {
if !self.origin.is_scheme_host_port_tuple() {
return Err(Error::Security);
}
let url = self.url();
let (tx, rx) = ipc::channel().unwrap();
let _ = self.window.resource_thread().send(GetCookiesForUrl((*url).clone(), tx, NonHTTP));
let cookies = rx.recv().unwrap();
@ -2513,10 +2536,11 @@ impl DocumentMethods for Document {
return Ok(());
}
let url = self.url();
if !is_scheme_host_port_tuple(url) {
if !self.origin.is_scheme_host_port_tuple() {
return Err(Error::Security);
}
let url = self.url();
let _ = self.window
.resource_thread()
.send(SetCookiesForUrl((*url).clone(), String::from(cookie), NonHTTP));
@ -2720,10 +2744,6 @@ impl DocumentMethods for Document {
}
}
fn is_scheme_host_port_tuple(url: &Url) -> bool {
url.host().is_some() && url.port_or_default().is_some()
}
fn update_with_current_time_ms(marker: &Cell<u64>) {
if marker.get() == Default::default() {
let time = time::get_time();

View File

@ -90,6 +90,7 @@ pub mod dom;
pub mod layout_interface;
mod mem;
mod network_listener;
pub mod origin;
pub mod page;
pub mod parse;
pub mod reporter;

View File

@ -0,0 +1,73 @@
/* 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/. */
use std::cell::RefCell;
use std::rc::Rc;
use url::{OpaqueOrigin, Origin as UrlOrigin};
use url::{Url, Host};
/// A representation of an [origin](https://html.spec.whatwg.org/multipage/#origin-2).
#[derive(HeapSizeOf)]
pub struct Origin {
#[ignore_heap_size_of = "Rc<T> has unclear ownership semantics"]
inner: Rc<RefCell<UrlOrigin>>,
}
// We can't use RefCell inside JSTraceable, but Origin doesn't contain JS values and
// DOMRefCell makes it much harder to write unit tests (due to setting up required TLS).
no_jsmanaged_fields!(Origin);
impl Origin {
/// Create a new origin comprising a unique, opaque identifier.
pub fn opaque_identifier() -> Origin {
let opaque = UrlOrigin::UID(OpaqueOrigin::new());
Origin {
inner: Rc::new(RefCell::new(opaque)),
}
}
/// Create a new origin for the given URL.
pub fn new(url: &Url) -> Origin {
Origin {
inner: Rc::new(RefCell::new(url.origin())),
}
}
pub fn set(&self, origin: UrlOrigin) {
*self.inner.borrow_mut() = origin;
}
/// Does this origin represent a host/scheme/port tuple?
pub fn is_scheme_host_port_tuple(&self) -> bool {
match *self.inner.borrow() {
UrlOrigin::Tuple(..) => true,
UrlOrigin::UID(..) => false,
}
}
/// Return the host associated with this origin.
pub fn host(&self) -> Option<Host> {
match *self.inner.borrow() {
UrlOrigin::Tuple(_, ref host, _) => Some(host.clone()),
UrlOrigin::UID(..) => None,
}
}
/// https://html.spec.whatwg.org/multipage/#same-origin
pub fn same_origin(&self, other: &Origin) -> bool {
*self.inner.borrow() == *other.inner.borrow()
}
pub fn copy(&self) -> Origin {
Origin {
inner: Rc::new(RefCell::new(self.inner.borrow().clone())),
}
}
pub fn alias(&self) -> Origin {
Origin {
inner: self.inner.clone(),
}
}
}

View File

@ -1752,7 +1752,9 @@ name = "script_tests"
version = "0.0.1"
dependencies = [
"msg 0.0.1",
"plugins 0.0.1",
"script 0.0.1",
"url 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
"util 0.0.1",
]

View File

@ -11,8 +11,14 @@ doctest = false
[dependencies.msg]
path = "../../../components/msg"
[dependencies.plugins]
path = "../../../components/plugins"
[dependencies.script]
path = "../../../components/script"
[dependencies.util]
path = "../../../components/util"
[dependencies]
url = {version = "0.5.8", features = ["heap_size"]}

View File

@ -2,10 +2,15 @@
* 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/. */
#![feature(plugin)]
#![plugin(plugins)]
extern crate msg;
extern crate script;
extern crate url;
extern crate util;
#[cfg(test)] mod origin;
#[cfg(all(test, target_pointer_width = "64"))] mod size_of;
#[cfg(test)] mod textinput;
#[cfg(test)] mod dom {

View File

@ -0,0 +1,105 @@
/* 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/. */
use script::origin::Origin;
#[test]
fn same_origin() {
let a = Origin::new(&url!("http://example.com/a.html"));
let b = Origin::new(&url!("http://example.com/b.html"));
assert!(a.same_origin(&b));
assert_eq!(a.is_scheme_host_port_tuple(), true);
}
#[test]
fn identical_origin() {
let a = Origin::new(&url!("http://example.com/a.html"));
assert!(a.same_origin(&a));
}
#[test]
fn cross_origin() {
let a = Origin::new(&url!("http://example.com/a.html"));
let b = Origin::new(&url!("http://example.org/b.html"));
assert!(!a.same_origin(&b));
}
#[test]
fn alias_same_origin() {
let a = Origin::new(&url!("http://example.com/a.html"));
let b = Origin::new(&url!("http://example.com/b.html"));
let c = b.alias();
assert!(a.same_origin(&c));
assert!(b.same_origin(&b));
assert!(c.same_origin(&b));
assert_eq!(c.is_scheme_host_port_tuple(), true);
}
#[test]
fn alias_cross_origin() {
let a = Origin::new(&url!("http://example.com/a.html"));
let b = Origin::new(&url!("http://example.org/b.html"));
let c = b.alias();
assert!(!a.same_origin(&c));
assert!(b.same_origin(&c));
assert!(c.same_origin(&c));
}
#[test]
fn alias_update_same_origin() {
let a = Origin::new(&url!("http://example.com/a.html"));
let b = Origin::new(&url!("http://example.org/b.html"));
let c = b.alias();
b.set(url!("http://example.com/c.html").origin());
assert!(a.same_origin(&c));
assert!(b.same_origin(&c));
assert!(c.same_origin(&c));
}
#[test]
fn alias_update_cross_origin() {
let a = Origin::new(&url!("http://example.com/a.html"));
let b = Origin::new(&url!("http://example.com/b.html"));
let c = b.alias();
b.set(url!("http://example.org/c.html").origin());
assert!(!a.same_origin(&c));
assert!(b.same_origin(&c));
assert!(c.same_origin(&c));
}
#[test]
fn alias_chain() {
let a = Origin::new(&url!("http://example.com/a.html"));
let b = Origin::new(&url!("http://example.com/b.html"));
let c = b.copy();
let d = c.alias();
let e = d.alias();
assert!(a.same_origin(&e));
assert!(b.same_origin(&e));
assert!(c.same_origin(&e));
assert!(d.same_origin(&e));
assert!(e.same_origin(&e));
c.set(url!("http://example.org/c.html").origin());
assert!(a.same_origin(&b));
assert!(!b.same_origin(&c));
assert!(c.same_origin(&d));
assert!(d.same_origin(&e));
assert!(!e.same_origin(&a));
}
#[test]
fn opaque() {
let a = Origin::opaque_identifier();
let b = Origin::opaque_identifier();
assert!(!a.same_origin(&b));
assert_eq!(a.is_scheme_host_port_tuple(), false);
}
#[test]
fn opaque_clone() {
let a = Origin::opaque_identifier();
let b = a.alias();
assert!(a.same_origin(&b));
assert_eq!(a.is_scheme_host_port_tuple(), false);
}