servo: Merge #15438 - Add ImmutableOrigin to allow for serializing origins (from asajeffrey:url-serializable-origin); r=jdm
authorConnor Brewster <connor.brewster@eagles.oc.edu>
Wed, 22 Feb 2017 09:46:27 -0800
changeset 373346 f7c0b8d47a9b9bfad434038e6043fb91a7c46f2a
parent 373345 4e3df6fb58ca02d1bbc0eab666d0d368db3bc4ac
child 373347 29fae4e7480ce3c36ee1a0b19970a449a21cec58
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
milestone54.0a1
servo: Merge #15438 - Add ImmutableOrigin to allow for serializing origins (from asajeffrey:url-serializable-origin); r=jdm <!-- Please describe your changes on the following line: --> This PR adds a serializable type `ImmutableOrigin` and a non-serializable type `MutableOrigin`. The immutable type represents an origin with `null` domain, and the mutable type represents an origin with a mutable domain. This separation is needed for implementing setting `document.domain`. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #14892. - [X] These changes do not require tests because it's a refactoring which will enable other features. <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 78e8c31a4d1890260dda83f2db85672f693c1e97
servo/Cargo.lock
servo/components/net/http_loader.rs
servo/components/net_traits/request.rs
servo/components/script/dom/bindings/trace.rs
servo/components/script/dom/document.rs
servo/components/script/dom/domimplementation.rs
servo/components/script/dom/domparser.rs
servo/components/script/dom/htmliframeelement.rs
servo/components/script/dom/node.rs
servo/components/script/dom/servoparser/mod.rs
servo/components/script/dom/window.rs
servo/components/script/dom/xmldocument.rs
servo/components/script/dom/xmlhttprequest.rs
servo/components/script/lib.rs
servo/components/script/origin.rs
servo/components/script/script_thread.rs
servo/components/url/Cargo.toml
servo/components/url/lib.rs
servo/components/url/origin.rs
servo/tests/unit/net/fetch.rs
servo/tests/unit/script/origin.rs
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -2591,18 +2591,21 @@ dependencies = [
 
 [[package]]
 name = "servo_url"
 version = "0.0.1"
 dependencies = [
  "heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo_rand 0.0.1",
  "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "url_serde 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "uuid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "sha1"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
--- a/servo/components/net/http_loader.rs
+++ b/servo/components/net/http_loader.rs
@@ -32,31 +32,30 @@ use msg::constellation_msg::PipelineId;
 use net_traits::{CookieSource, FetchMetadata, NetworkError, ReferrerPolicy};
 use net_traits::hosts::replace_hosts;
 use net_traits::request::{CacheMode, CredentialsMode, Destination, Origin};
 use net_traits::request::{RedirectMode, Referrer, Request, RequestMode, ResponseTainting};
 use net_traits::response::{HttpsState, Response, ResponseBody, ResponseType};
 use openssl;
 use openssl::ssl::error::{OpensslError, SslError};
 use resource_thread::AuthCache;
-use servo_url::ServoUrl;
+use servo_url::{ImmutableOrigin, ServoUrl};
 use std::collections::HashSet;
 use std::error::Error;
 use std::io::{self, Read, Write};
 use std::iter::FromIterator;
 use std::mem;
 use std::ops::Deref;
 use std::rc::Rc;
 use std::sync::{Arc, RwLock};
 use std::sync::mpsc::{channel, Sender};
 use std::thread;
 use time;
 use time::Tm;
 use unicase::UniCase;
-use url::Origin as UrlOrigin;
 use uuid;
 
 fn read_block<R: Read>(reader: &mut R) -> Result<Data, ()> {
     let mut buf = vec![0; 1024];
 
     match reader.read(&mut buf) {
         Ok(len) if len > 0 => {
             buf.truncate(len);
@@ -384,17 +383,17 @@ fn send_response_to_devtools(devtools_ch
                              pipeline_id: PipelineId) {
     let response = DevtoolsHttpResponse { headers: headers, status: status, body: None, pipeline_id: pipeline_id };
     let net_event_response = NetworkEvent::HttpResponse(response);
 
     let msg = ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event_response);
     let _ = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg));
 }
 
-fn auth_from_cache(auth_cache: &Arc<RwLock<AuthCache>>, origin: &UrlOrigin) -> Option<Basic> {
+fn auth_from_cache(auth_cache: &Arc<RwLock<AuthCache>>, origin: &ImmutableOrigin) -> Option<Basic> {
     if let Some(ref auth_entry) = auth_cache.read().unwrap().entries.get(&origin.ascii_serialization()) {
         let user_name = auth_entry.user_name.clone();
         let password  = Some(auth_entry.password.clone());
         Some(Basic { username: user_name, password: password })
     } else {
         None
     }
 }
@@ -762,17 +761,17 @@ fn http_redirect_fetch(request: Rc<Reque
 
     // Step 8
     if cors_flag && has_credentials {
         return Response::network_error(NetworkError::Internal("Credentials check failed".into()));
     }
 
     // Step 9
     if cors_flag && !same_origin {
-        *request.origin.borrow_mut() = Origin::Origin(UrlOrigin::new_opaque());
+        *request.origin.borrow_mut() = Origin::Origin(ImmutableOrigin::new_opaque());
     }
 
     // Step 10
     let status_code = response.actual_response().status.unwrap();
     if ((status_code == StatusCode::MovedPermanently || status_code == StatusCode::Found) &&
         *request.method.borrow() == Method::Post) ||
         status_code == StatusCode::SeeOther {
         *request.method.borrow_mut() = Method::Get;
--- a/servo/components/net_traits/request.rs
+++ b/servo/components/net_traits/request.rs
@@ -1,20 +1,19 @@
 /* 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 ReferrerPolicy;
 use hyper::header::Headers;
 use hyper::method::Method;
 use msg::constellation_msg::PipelineId;
-use servo_url::ServoUrl;
+use servo_url::{ImmutableOrigin, ServoUrl};
 use std::cell::{Cell, RefCell};
 use std::default::Default;
-use url::Origin as UrlOrigin;
 
 /// An [initiator](https://fetch.spec.whatwg.org/#concept-request-initiator)
 #[derive(Copy, Clone, PartialEq, HeapSizeOf)]
 pub enum Initiator {
     None,
     Download,
     ImageSet,
     Manifest,
@@ -50,20 +49,20 @@ pub enum Destination {
     ServiceWorker,
     SharedWorker,
     Style,
     Worker,
     XSLT,
 }
 
 /// A request [origin](https://fetch.spec.whatwg.org/#concept-request-origin)
-#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
+#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, HeapSizeOf)]
 pub enum Origin {
     Client,
-    Origin(UrlOrigin),
+    Origin(ImmutableOrigin),
 }
 
 /// A [referer](https://fetch.spec.whatwg.org/#concept-request-referrer)
 #[derive(Clone, PartialEq, HeapSizeOf)]
 pub enum Referrer {
     NoReferrer,
     /// Default referrer if nothing is specified
     Client,
--- a/servo/components/script/dom/bindings/trace.rs
+++ b/servo/components/script/dom/bindings/trace.rs
@@ -74,17 +74,17 @@ use profile_traits::time::ProfilerChan a
 use script_layout_interface::OpaqueStyleAndLayoutData;
 use script_layout_interface::reporter::CSSErrorReporter;
 use script_layout_interface::rpc::LayoutRPC;
 use script_traits::{DocumentActivity, TimerEventId, TimerSource, TouchpadPressurePhase};
 use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
 use selectors::matching::ElementSelectorFlags;
 use serde::{Deserialize, Serialize};
 use servo_atoms::Atom;
-use servo_url::ServoUrl;
+use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
 use smallvec::SmallVec;
 use std::cell::{Cell, RefCell, UnsafeCell};
 use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
 use std::hash::{BuildHasher, Hash};
 use std::ops::{Deref, DerefMut};
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::sync::{Arc, Mutex};
@@ -99,17 +99,16 @@ use style::keyframes::Keyframe;
 use style::media_queries::MediaList;
 use style::properties::PropertyDeclarationBlock;
 use style::selector_parser::{PseudoElement, Snapshot};
 use style::stylesheets::{CssRules, KeyframesRule, MediaRule, NamespaceRule, StyleRule, ImportRule};
 use style::stylesheets::SupportsRule;
 use style::values::specified::Length;
 use style::viewport::ViewportRule;
 use time::Duration;
-use url::Origin as UrlOrigin;
 use uuid::Uuid;
 use webrender_traits::{WebGLBufferId, WebGLError, WebGLFramebufferId, WebGLProgramId};
 use webrender_traits::{WebGLRenderbufferId, WebGLShaderId, WebGLTextureId};
 
 /// A trait to allow tracing (only) DOM objects.
 pub unsafe trait JSTraceable {
     /// Trace `self`.
     unsafe fn trace(&self, trc: *mut JSTracer);
@@ -312,19 +311,20 @@ unsafe impl<A: JSTraceable, B: JSTraceab
     unsafe fn trace(&self, trc: *mut JSTracer) {
         let (ref a, ref b, ref c) = *self;
         a.trace(trc);
         b.trace(trc);
         c.trace(trc);
     }
 }
 
-unsafe_no_jsmanaged_fields!(bool, f32, f64, String, ServoUrl, AtomicBool, AtomicUsize, UrlOrigin, Uuid, char);
+unsafe_no_jsmanaged_fields!(bool, f32, f64, String, AtomicBool, AtomicUsize, Uuid, char);
 unsafe_no_jsmanaged_fields!(usize, u8, u16, u32, u64);
 unsafe_no_jsmanaged_fields!(isize, i8, i16, i32, i64);
+unsafe_no_jsmanaged_fields!(ServoUrl, ImmutableOrigin, MutableOrigin);
 unsafe_no_jsmanaged_fields!(Image, ImageMetadata, ImageCacheChan, ImageCacheThread);
 unsafe_no_jsmanaged_fields!(Metadata);
 unsafe_no_jsmanaged_fields!(NetworkError);
 unsafe_no_jsmanaged_fields!(Atom, Prefix, LocalName, Namespace, QualName);
 unsafe_no_jsmanaged_fields!(TrustedPromise);
 unsafe_no_jsmanaged_fields!(PropertyDeclarationBlock);
 // These three are interdependent, if you plan to put jsmanaged data
 // in one of these make sure it is propagated properly to containing structs
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -100,28 +100,27 @@ use js::jsapi::JS_GetRuntime;
 use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER};
 use msg::constellation_msg::{FrameId, Key, KeyModifiers, KeyState};
 use net_traits::{FetchResponseMsg, IpcSend, ReferrerPolicy};
 use net_traits::CookieSource::NonHTTP;
 use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl};
 use net_traits::request::RequestInit;
 use net_traits::response::HttpsState;
 use num_traits::ToPrimitive;
-use origin::Origin;
 use script_layout_interface::message::{Msg, ReflowQueryType};
 use script_runtime::{CommonScriptMsg, ScriptThreadEventCategory};
 use script_thread::{MainThreadScriptMsg, Runnable};
 use script_traits::{AnimationState, CompositorEvent, DocumentActivity};
 use script_traits::{MouseButton, MouseEventType, MozBrowserEvent};
 use script_traits::{ScriptMsg as ConstellationMsg, TouchpadPressurePhase};
 use script_traits::{TouchEventType, TouchId};
 use script_traits::UntrustedNodeAddress;
 use servo_atoms::Atom;
 use servo_config::prefs::PREFS;
-use servo_url::ServoUrl;
+use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
 use std::ascii::AsciiExt;
 use std::borrow::ToOwned;
 use std::cell::{Cell, Ref, RefMut};
 use std::collections::{HashMap, VecDeque};
 use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::default::Default;
 use std::iter::once;
 use std::mem;
@@ -131,16 +130,17 @@ use std::time::{Duration, Instant};
 use style::attr::AttrValue;
 use style::context::{QuirksMode, ReflowGoal};
 use style::restyle_hints::{RestyleHint, RESTYLE_STYLE_ATTRIBUTE};
 use style::selector_parser::{RestyleDamage, Snapshot};
 use style::str::{HTML_SPACE_CHARACTERS, split_html_space_chars, str_join};
 use style::stylesheets::Stylesheet;
 use task_source::TaskSource;
 use time;
+use url::Host;
 use url::percent_encoding::percent_decode;
 
 pub enum TouchEventResult {
     Processed(bool),
     Forwarded,
 }
 
 #[derive(Clone, Copy, Debug, HeapSizeOf, JSTraceable, PartialEq)]
@@ -272,17 +272,17 @@ pub struct Document {
     dom_content_loaded_event_end: Cell<u64>,
     dom_complete: Cell<u64>,
     load_event_start: Cell<u64>,
     load_event_end: Cell<u64>,
     /// 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,
+    origin: MutableOrigin,
     ///  https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-states
     referrer_policy: Cell<Option<ReferrerPolicy>>,
     /// https://html.spec.whatwg.org/multipage/#dom-document-referrer
     referrer: Option<String>,
     /// https://html.spec.whatwg.org/multipage/#target-element
     target_element: MutNullableJS<Element>,
     /// https://w3c.github.io/uievents/#event-type-dblclick
     #[ignore_heap_size_of = "Defined in std"]
@@ -419,17 +419,17 @@ impl Document {
                 );
                 self.window().resume();
             } else {
                 self.window().suspend();
             }
         }
     }
 
-    pub fn origin(&self) -> &Origin {
+    pub fn origin(&self) -> &MutableOrigin {
         &self.origin
     }
 
     // https://dom.spec.whatwg.org/#concept-document-url
     pub fn url(&self) -> ServoUrl {
         self.url.borrow().clone()
     }
 
@@ -1944,17 +1944,17 @@ pub enum HasBrowsingContext {
     No,
     Yes,
 }
 
 impl Document {
     pub fn new_inherited(window: &Window,
                          has_browsing_context: HasBrowsingContext,
                          url: Option<ServoUrl>,
-                         origin: Origin,
+                         origin: MutableOrigin,
                          is_html_document: IsHTMLDocument,
                          content_type: Option<DOMString>,
                          last_modified: Option<String>,
                          activity: DocumentActivity,
                          source: DocumentSource,
                          doc_loader: DocumentLoader,
                          referrer: Option<String>,
                          referrer_policy: Option<ReferrerPolicy>)
@@ -2048,31 +2048,31 @@ impl Document {
 
     // https://dom.spec.whatwg.org/#dom-document-document
     pub fn Constructor(window: &Window) -> Fallible<Root<Document>> {
         let doc = window.Document();
         let docloader = DocumentLoader::new(&*doc.loader());
         Ok(Document::new(window,
                          HasBrowsingContext::No,
                          None,
-                         doc.origin().alias(),
+                         doc.origin().clone(),
                          IsHTMLDocument::NonHTMLDocument,
                          None,
                          None,
                          DocumentActivity::Inactive,
                          DocumentSource::NotFromParser,
                          docloader,
                          None,
                          None))
     }
 
     pub fn new(window: &Window,
                has_browsing_context: HasBrowsingContext,
                url: Option<ServoUrl>,
-               origin: Origin,
+               origin: MutableOrigin,
                doctype: IsHTMLDocument,
                content_type: Option<DOMString>,
                last_modified: Option<String>,
                activity: DocumentActivity,
                source: DocumentSource,
                doc_loader: DocumentLoader,
                referrer: Option<String>,
                referrer_policy: Option<ReferrerPolicy>)
@@ -2149,17 +2149,17 @@ impl Document {
                 IsHTMLDocument::HTMLDocument
             } else {
                 IsHTMLDocument::NonHTMLDocument
             };
             let new_doc = Document::new(self.window(),
                                         HasBrowsingContext::No,
                                         None,
                                         // https://github.com/whatwg/html/issues/2109
-                                        Origin::opaque_identifier(),
+                                        MutableOrigin::new(ImmutableOrigin::new_opaque()),
                                         doctype,
                                         None,
                                         None,
                                         DocumentActivity::Inactive,
                                         DocumentSource::NotFromParser,
                                         DocumentLoader::new(&self.loader()),
                                         None,
                                         None);
@@ -2406,26 +2406,27 @@ impl DocumentMethods for Document {
         }
         // TODO Step 3.
         false
     }
 
     // https://html.spec.whatwg.org/multipage/#relaxing-the-same-origin-restriction
     fn Domain(&self) -> DOMString {
         // Step 1.
-        if self.browsing_context().is_none() {
+        if !self.has_browsing_context {
             return DOMString::new();
         }
 
-        if let Some(host) = self.origin.host() {
+        // Step 2.
+        match self.origin.effective_domain() {
+            // Step 3.
+            None => DOMString::new(),
             // Step 4.
-            DOMString::from(host.to_string())
-        } else {
-            // Step 3.
-            DOMString::new()
+            Some(Host::Domain(domain)) => DOMString::from(domain),
+            Some(host) => DOMString::from(host.to_string()),
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-document-referrer
     fn Referrer(&self) -> DOMString {
         match self.referrer {
             Some(ref referrer) => DOMString::from(referrer.to_string()),
             None => DOMString::new()
@@ -3072,17 +3073,17 @@ impl DocumentMethods for Document {
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-document-cookie
     fn GetCookie(&self) -> Fallible<DOMString> {
         if self.is_cookie_averse() {
             return Ok(DOMString::new());
         }
 
-        if !self.origin.is_scheme_host_port_tuple() {
+        if !self.origin.is_tuple() {
             return Err(Error::Security);
         }
 
         let url = self.url();
         let (tx, rx) = ipc::channel().unwrap();
         let _ = self.window
             .upcast::<GlobalScope>()
             .resource_threads()
@@ -3092,17 +3093,17 @@ impl DocumentMethods for Document {
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-document-cookie
     fn SetCookie(&self, cookie: DOMString) -> ErrorResult {
         if self.is_cookie_averse() {
             return Ok(());
         }
 
-        if !self.origin.is_scheme_host_port_tuple() {
+        if !self.origin.is_tuple() {
             return Err(Error::Security);
         }
 
         let header = Header::parse_header(&[cookie.into()]);
         if let Ok(SetCookie(cookies)) = header {
             let cookies = cookies.into_iter().map(Serde).collect();
             let _ = self.window
                         .upcast::<GlobalScope>()
--- a/servo/components/script/dom/domimplementation.rs
+++ b/servo/components/script/dom/domimplementation.rs
@@ -75,17 +75,17 @@ impl DOMImplementationMethods for DOMImp
             ns!(svg) => "image/svg+xml",
             _ => "application/xml"
         };
 
         // Step 1.
         let doc = XMLDocument::new(win,
                                    HasBrowsingContext::No,
                                    None,
-                                   self.document.origin().alias(),
+                                   self.document.origin().clone(),
                                    IsHTMLDocument::NonHTMLDocument,
                                    Some(DOMString::from(content_type)),
                                    None,
                                    DocumentActivity::Inactive,
                                    DocumentSource::NotFromParser,
                                    loader);
         // Step 2-3.
         let maybe_elem = if qname.is_empty() {
@@ -122,17 +122,17 @@ impl DOMImplementationMethods for DOMImp
     fn CreateHTMLDocument(&self, title: Option<DOMString>) -> Root<Document> {
         let win = self.document.window();
         let loader = DocumentLoader::new(&self.document.loader());
 
         // Step 1-2.
         let doc = Document::new(win,
                                 HasBrowsingContext::No,
                                 None,
-                                self.document.origin().alias(),
+                                self.document.origin().clone(),
                                 IsHTMLDocument::HTMLDocument,
                                 None,
                                 None,
                                 DocumentActivity::Inactive,
                                 DocumentSource::NotFromParser,
                                 loader,
                                 None,
                                 None);
--- a/servo/components/script/dom/domparser.rs
+++ b/servo/components/script/dom/domparser.rs
@@ -56,17 +56,17 @@ impl DOMParserMethods for DOMParser {
         let content_type = DOMString::from(ty.as_str());
         let doc = self.window.Document();
         let loader = DocumentLoader::new(&*doc.loader());
         match ty {
             Text_html => {
                 let document = Document::new(&self.window,
                                              HasBrowsingContext::No,
                                              Some(url.clone()),
-                                             doc.origin().alias(),
+                                             doc.origin().clone(),
                                              IsHTMLDocument::HTMLDocument,
                                              Some(content_type),
                                              None,
                                              DocumentActivity::Inactive,
                                              DocumentSource::FromParser,
                                              loader,
                                              None,
                                              None);
@@ -74,17 +74,17 @@ impl DOMParserMethods for DOMParser {
                 document.set_ready_state(DocumentReadyState::Complete);
                 Ok(document)
             }
             Text_xml | Application_xml | Application_xhtml_xml => {
                 // FIXME: this should probably be FromParser when we actually parse the string (#3756).
                 let document = Document::new(&self.window,
                                              HasBrowsingContext::No,
                                              Some(url.clone()),
-                                             doc.origin().alias(),
+                                             doc.origin().clone(),
                                              IsHTMLDocument::NonHTMLDocument,
                                              Some(content_type),
                                              None,
                                              DocumentActivity::Inactive,
                                              DocumentSource::NotFromParser,
                                              loader,
                                              None,
                                              None);
--- a/servo/components/script/dom/htmliframeelement.rs
+++ b/servo/components/script/dom/htmliframeelement.rs
@@ -162,17 +162,17 @@ impl HTMLIFrameElement {
                 frame_id: self.frame_id,
                 load_data: load_data.unwrap(),
                 pipeline_port: pipeline_receiver,
                 content_process_shutdown_chan: None,
                 window_size: None,
                 layout_threads: PREFS.get("layout.threads").as_u64().expect("count") as usize,
             };
 
-            ScriptThread::process_attach_layout(new_layout_info, document.origin().alias());
+            ScriptThread::process_attach_layout(new_layout_info, document.origin().clone());
         } else {
             let load_info = IFrameLoadInfoWithData {
                 info: load_info,
                 load_data: load_data,
                 old_pipeline_id: old_pipeline_id,
                 sandbox: sandboxed,
             };
             global_scope
--- a/servo/components/script/dom/node.rs
+++ b/servo/components/script/dom/node.rs
@@ -1724,17 +1724,17 @@ impl Node {
                 } else {
                     IsHTMLDocument::NonHTMLDocument
                 };
                 let window = document.window();
                 let loader = DocumentLoader::new(&*document.loader());
                 let document = Document::new(window, HasBrowsingContext::No,
                                              Some(document.url()),
                                              // https://github.com/whatwg/dom/issues/378
-                                             document.origin().alias(),
+                                             document.origin().clone(),
                                              is_html_doc, None,
                                              None, DocumentActivity::Inactive,
                                              DocumentSource::NotFromParser, loader,
                                              None, None);
                 Root::upcast::<Node>(document)
             },
             NodeTypeId::Element(..) => {
                 let element = node.downcast::<Element>().unwrap();
--- a/servo/components/script/dom/servoparser/mod.rs
+++ b/servo/components/script/dom/servoparser/mod.rs
@@ -105,17 +105,17 @@ impl ServoParser {
         let url = context_document.url();
 
         // Step 1.
         let loader = DocumentLoader::new_with_threads(context_document.loader().resource_threads().clone(),
                                                       Some(url.clone()));
         let document = Document::new(window,
                                      HasBrowsingContext::No,
                                      Some(url.clone()),
-                                     context_document.origin().alias(),
+                                     context_document.origin().clone(),
                                      IsHTMLDocument::HTMLDocument,
                                      None,
                                      None,
                                      DocumentActivity::Inactive,
                                      DocumentSource::FromParser,
                                      loader,
                                      None,
                                      None);
--- a/servo/components/script/dom/window.rs
+++ b/servo/components/script/dom/window.rs
@@ -57,17 +57,16 @@ use js::jsapi::{JS_GC, JS_GetRuntime};
 use js::jsval::UndefinedValue;
 use js::rust::Runtime;
 use msg::constellation_msg::{FrameType, PipelineId};
 use net_traits::{ResourceThreads, ReferrerPolicy};
 use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
 use net_traits::storage_thread::StorageType;
 use num_traits::ToPrimitive;
 use open;
-use origin::Origin;
 use profile_traits::mem::ProfilerChan as MemProfilerChan;
 use profile_traits::time::ProfilerChan as TimeProfilerChan;
 use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64};
 use script_layout_interface::TrustedNodeAddress;
 use script_layout_interface::message::{Msg, Reflow, ReflowQueryType, ScriptReflow};
 use script_layout_interface::reporter::CSSErrorReporter;
 use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC};
 use script_layout_interface::rpc::{MarginStyleResponse, NodeScrollRootIdResponse};
@@ -78,17 +77,17 @@ use script_thread::SendableMainThreadScr
 use script_traits::{ConstellationControlMsg, LoadData, MozBrowserEvent, UntrustedNodeAddress};
 use script_traits::{DocumentState, TimerEvent, TimerEventId};
 use script_traits::{ScriptMsg as ConstellationMsg, TimerEventRequest, WindowSizeData, WindowSizeType};
 use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
 use servo_atoms::Atom;
 use servo_config::opts;
 use servo_config::prefs::PREFS;
 use servo_geometry::{f32_rect_to_au_rect, max_rect};
-use servo_url::ServoUrl;
+use servo_url::{ImmutableOrigin, ServoUrl};
 use std::ascii::AsciiExt;
 use std::borrow::ToOwned;
 use std::cell::Cell;
 use std::collections::{HashMap, HashSet};
 use std::default::Default;
 use std::io::{Write, stderr, stdout};
 use std::mem;
 use std::rc::Rc;
@@ -655,20 +654,20 @@ impl WindowMethods for Window {
                    origin: DOMString)
                    -> ErrorResult {
         // Step 3-5.
         let origin = match &origin[..] {
             "*" => None,
             "/" => {
                 // TODO(#12715): Should be the origin of the incumbent settings
                 //               object, not self's.
-                Some(self.Document().origin().copy())
+                Some(self.Document().origin().immutable().clone())
             },
             url => match ServoUrl::parse(&url) {
-                Ok(url) => Some(Origin::new(&url)),
+                Ok(url) => Some(url.origin().clone()),
                 Err(_) => return Err(Error::Syntax),
             }
         };
 
         // Step 1-2, 6-8.
         // TODO(#12717): Should implement the `transfer` argument.
         let data = try!(StructuredCloneData::write(cx, message));
 
@@ -1788,23 +1787,23 @@ fn debug_reflow_events(id: PipelineId, g
         ReflowReason::ElementStateChanged => "\tElementStateChanged",
     });
 
     println!("{}", debug_msg);
 }
 
 struct PostMessageHandler {
     destination: Trusted<Window>,
-    origin: Option<Origin>,
+    origin: Option<ImmutableOrigin>,
     message: StructuredCloneData,
 }
 
 impl PostMessageHandler {
     fn new(window: &Window,
-           origin: Option<Origin>,
+           origin: Option<ImmutableOrigin>,
            message: StructuredCloneData) -> PostMessageHandler {
         PostMessageHandler {
             destination: Trusted::new(window),
             origin: origin,
             message: message,
         }
     }
 }
--- a/servo/components/script/dom/xmldocument.rs
+++ b/servo/components/script/dom/xmldocument.rs
@@ -10,31 +10,30 @@ use dom::bindings::inheritance::Castable
 use dom::bindings::js::Root;
 use dom::bindings::reflector::reflect_dom_object;
 use dom::bindings::str::DOMString;
 use dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument};
 use dom::location::Location;
 use dom::node::Node;
 use dom::window::Window;
 use js::jsapi::{JSContext, JSObject};
-use origin::Origin;
 use script_traits::DocumentActivity;
-use servo_url::ServoUrl;
+use servo_url::{MutableOrigin, ServoUrl};
 
 // https://dom.spec.whatwg.org/#xmldocument
 #[dom_struct]
 pub struct XMLDocument {
     document: Document,
 }
 
 impl XMLDocument {
     fn new_inherited(window: &Window,
                      has_browsing_context: HasBrowsingContext,
                      url: Option<ServoUrl>,
-                     origin: Origin,
+                     origin: MutableOrigin,
                      is_html_document: IsHTMLDocument,
                      content_type: Option<DOMString>,
                      last_modified: Option<String>,
                      activity: DocumentActivity,
                      source: DocumentSource,
                      doc_loader: DocumentLoader) -> XMLDocument {
         XMLDocument {
             document: Document::new_inherited(window,
@@ -50,17 +49,17 @@ impl XMLDocument {
                                               None,
                                               None),
         }
     }
 
     pub fn new(window: &Window,
                has_browsing_context: HasBrowsingContext,
                url: Option<ServoUrl>,
-               origin: Origin,
+               origin: MutableOrigin,
                doctype: IsHTMLDocument,
                content_type: Option<DOMString>,
                last_modified: Option<String>,
                activity: DocumentActivity,
                source: DocumentSource,
                doc_loader: DocumentLoader)
                -> Root<XMLDocument> {
         let doc = reflect_dom_object(
--- a/servo/components/script/dom/xmlhttprequest.rs
+++ b/servo/components/script/dom/xmlhttprequest.rs
@@ -1220,17 +1220,17 @@ impl XMLHttpRequest {
         };
         let mime_type = self.final_mime_type();
         let content_type = mime_type.map(|mime|{
             DOMString::from(format!("{}", mime))
         });
         Document::new(win,
                       HasBrowsingContext::No,
                       parsed_url,
-                      doc.origin().alias(),
+                      doc.origin().clone(),
                       is_html_document,
                       content_type,
                       None,
                       DocumentActivity::Inactive,
                       DocumentSource::FromParser,
                       docloader,
                       None,
                       None)
--- a/servo/components/script/lib.rs
+++ b/servo/components/script/lib.rs
@@ -109,17 +109,16 @@ mod devtools;
 pub mod document_loader;
 #[macro_use]
 mod dom;
 pub mod fetch;
 pub mod layout_wrapper;
 mod mem;
 mod microtask;
 mod network_listener;
-pub mod origin;
 pub mod script_runtime;
 #[allow(unsafe_code)]
 pub mod script_thread;
 mod serviceworker_manager;
 mod serviceworkerjob;
 mod stylesheet_loader;
 mod task_source;
 pub mod test;
deleted file mode 100644
--- a/servo/components/script/origin.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-/* 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 servo_url::ServoUrl;
-use std::sync::Arc;
-use url::Host;
-use url::Origin as UrlOrigin;
-
-/// A representation of an [origin](https://html.spec.whatwg.org/multipage/#origin-2).
-#[derive(HeapSizeOf, JSTraceable)]
-pub struct Origin {
-    #[ignore_heap_size_of = "Arc<T> has unclear ownership semantics"]
-    inner: Arc<UrlOrigin>,
-}
-
-impl Origin {
-    /// Create a new origin comprising a unique, opaque identifier.
-    pub fn opaque_identifier() -> Origin {
-        Origin {
-            inner: Arc::new(UrlOrigin::new_opaque()),
-        }
-    }
-
-    /// Create a new origin for the given URL.
-    pub fn new(url: &ServoUrl) -> Origin {
-        Origin {
-            inner: Arc::new(url.origin()),
-        }
-    }
-
-    /// Does this origin represent a host/scheme/port tuple?
-    pub fn is_scheme_host_port_tuple(&self) -> bool {
-        self.inner.is_tuple()
-    }
-
-    /// Return the host associated with this origin.
-    pub fn host(&self) -> Option<&Host<String>> {
-        match *self.inner {
-            UrlOrigin::Tuple(_, ref host, _) => Some(host),
-            UrlOrigin::Opaque(..) => None,
-        }
-    }
-
-    /// https://html.spec.whatwg.org/multipage/#same-origin
-    pub fn same_origin(&self, other: &Origin) -> bool {
-        self.inner == other.inner
-    }
-
-    pub fn copy(&self) -> Origin {
-        Origin {
-            inner: Arc::new((*self.inner).clone()),
-        }
-    }
-
-    pub fn alias(&self) -> Origin {
-        Origin {
-            inner: self.inner.clone(),
-        }
-    }
-}
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -72,17 +72,16 @@ use mem::heap_size_of_self_and_children;
 use microtask::{MicrotaskQueue, Microtask};
 use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespace};
 use net_traits::{CoreResourceMsg, FetchMetadata, FetchResponseListener};
 use net_traits::{IpcSend, Metadata, ReferrerPolicy, ResourceThreads};
 use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread};
 use net_traits::request::{CredentialsMode, Destination, RequestInit};
 use net_traits::storage_thread::StorageType;
 use network_listener::NetworkListener;
-use origin::Origin;
 use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan};
 use profile_traits::time::{self, ProfilerCategory, profile};
 use script_layout_interface::message::{self, NewLayoutThreadInfo, ReflowQueryType};
 use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
 use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx};
 use script_traits::{CompositorEvent, ConstellationControlMsg};
 use script_traits::{DocumentActivity, DiscardBrowsingContext, EventResult};
 use script_traits::{InitialScriptState, LayoutMsg, LoadData, MouseButton, MouseEventType, MozBrowserEvent};
@@ -90,17 +89,17 @@ use script_traits::{NewLayoutInfo, Scrip
 use script_traits::{ScriptThreadFactory, TimerEvent, TimerEventRequest, TimerSource};
 use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
 use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent};
 use script_traits::CompositorEvent::{TouchEvent, TouchpadPressureEvent};
 use script_traits::WebVREventMsg;
 use script_traits::webdriver_msg::WebDriverScriptCommand;
 use serviceworkerjob::{Job, JobQueue, AsyncJobHandler};
 use servo_config::opts;
-use servo_url::ServoUrl;
+use servo_url::{MutableOrigin, ServoUrl};
 use std::cell::Cell;
 use std::collections::{hash_map, HashMap, HashSet};
 use std::default::Default;
 use std::ops::Deref;
 use std::option::Option;
 use std::ptr;
 use std::rc::Rc;
 use std::result::Result;
@@ -150,28 +149,29 @@ struct InProgressLoad {
     /// Channel to the layout thread associated with this pipeline.
     layout_chan: Sender<message::Msg>,
     /// The activity level of the document (inactive, active or fully active).
     activity: DocumentActivity,
     /// Window is visible.
     is_visible: bool,
     /// The requested URL of the load.
     url: ServoUrl,
-    origin: Origin,
+    /// The origin for the document
+    origin: MutableOrigin,
 }
 
 impl InProgressLoad {
     /// Create a new InProgressLoad object.
     fn new(id: PipelineId,
            frame_id: FrameId,
            parent_info: Option<(PipelineId, FrameType)>,
            layout_chan: Sender<message::Msg>,
            window_size: Option<WindowSizeData>,
            url: ServoUrl,
-           origin: Origin) -> InProgressLoad {
+           origin: MutableOrigin) -> InProgressLoad {
         InProgressLoad {
             pipeline_id: id,
             frame_id: frame_id,
             parent_info: parent_info,
             layout_chan: layout_chan,
             window_size: window_size,
             activity: DocumentActivity::FullyActive,
             is_visible: true,
@@ -540,17 +540,17 @@ impl ScriptThreadFactory for ScriptThrea
                                                   script_chan.clone());
 
             SCRIPT_THREAD_ROOT.with(|root| {
                 root.set(Some(&script_thread as *const _));
             });
 
             let mut failsafe = ScriptMemoryFailsafe::new(&script_thread);
 
-            let origin = Origin::new(&load_data.url);
+            let origin = MutableOrigin::new(load_data.url.origin());
             let new_load = InProgressLoad::new(id, frame_id, parent_info, layout_chan, window_size,
                                                load_data.url.clone(), origin);
             script_thread.start_page_load(new_load, load_data);
 
             let reporter_name = format!("script-reporter-{}", id);
             mem_profiler_chan.run_with_memory_reporting(|| {
                 script_thread.start();
                 let _ = script_thread.content_process_shutdown_chan.send(());
@@ -606,17 +606,17 @@ impl ScriptThread {
                 let script_thread = unsafe { &*script_thread };
                 let _ = script_thread.chan.send(CommonScriptMsg::RunnableMsg(
                     ScriptThreadEventCategory::DomEvent,
                     box task));
             }
         });
     }
 
-    pub fn process_attach_layout(new_layout_info: NewLayoutInfo, origin: Origin) {
+    pub fn process_attach_layout(new_layout_info: NewLayoutInfo, origin: MutableOrigin) {
         SCRIPT_THREAD_ROOT.with(|root| {
             if let Some(script_thread) = root.get() {
                 let script_thread = unsafe { &*script_thread };
                 script_thread.profile_event(ScriptThreadEventCategory::AttachLayout, || {
                     script_thread.handle_new_layout(new_layout_info, origin);
                 })
             }
         });
@@ -786,17 +786,17 @@ impl ScriptThread {
             // https://html.spec.whatwg.org/multipage/#event-loop-processing-model step 7
             match event {
                 // This has to be handled before the ResizeMsg below,
                 // otherwise the page may not have been added to the
                 // child list yet, causing the find() to fail.
                 FromConstellation(ConstellationControlMsg::AttachLayout(
                         new_layout_info)) => {
                     self.profile_event(ScriptThreadEventCategory::AttachLayout, || {
-                        let origin = Origin::new(&new_layout_info.load_data.url);
+                        let origin = MutableOrigin::new(new_layout_info.load_data.url.origin());
                         self.handle_new_layout(new_layout_info, origin);
                     })
                 }
                 FromConstellation(ConstellationControlMsg::Resize(id, size, size_type)) => {
                     // step 7.7
                     self.profile_event(ScriptThreadEventCategory::Resize, || {
                         self.handle_resize(id, size, size_type);
                     })
@@ -1203,17 +1203,17 @@ impl ScriptThread {
             } else {
                 scroll_offsets.insert(node_address,
                                       Point2D::new(-scroll_offset.x, -scroll_offset.y));
             }
         }
         window.set_scroll_offsets(scroll_offsets)
     }
 
-    fn handle_new_layout(&self, new_layout_info: NewLayoutInfo, origin: Origin) {
+    fn handle_new_layout(&self, new_layout_info: NewLayoutInfo, origin: MutableOrigin) {
         let NewLayoutInfo {
             parent_info,
             new_pipeline_id,
             frame_id,
             load_data,
             window_size,
             pipeline_port,
             content_process_shutdown_chan,
--- a/servo/components/url/Cargo.toml
+++ b/servo/components/url/Cargo.toml
@@ -5,16 +5,19 @@ authors = ["The Servo Project Developers
 license = "MPL-2.0"
 publish = false
 
 [lib]
 name = "servo_url"
 path = "lib.rs"
 
 [features]
-servo = ["heapsize", "heapsize_derive", "serde", "url/heap_size", "url_serde"]
+servo = ["heapsize", "heapsize_derive", "serde", "serde_derive", "uuid/serde", "url/heap_size", "url_serde"]
 
 [dependencies]
 heapsize = {version = "0.3.0", optional = true}
 heapsize_derive = {version = "0.1", optional = true}
 serde = {version = "0.9", optional = true}
+serde_derive = {version = "0.9", optional = true}
+servo_rand = {path = "../rand"}
 url = "1.2"
-url_serde = {version = "0.1", optional = true}
+url_serde = {version = "0.1.3", optional = true}
+uuid = {version = "0.4.0", features = ["v4"]}
--- a/servo/components/url/lib.rs
+++ b/servo/components/url/lib.rs
@@ -2,29 +2,36 @@
  * 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/. */
 
 #![deny(unsafe_code)]
 
 #![crate_name = "servo_url"]
 #![crate_type = "rlib"]
 
-#[cfg(feature = "servo")] extern crate heapsize;
+#[cfg(feature = "servo")] #[macro_use] extern crate heapsize;
 #[cfg(feature = "servo")] #[macro_use] extern crate heapsize_derive;
 #[cfg(feature = "servo")] extern crate serde;
+#[cfg(feature = "servo")] #[macro_use] extern crate serde_derive;
 #[cfg(feature = "servo")] extern crate url_serde;
 
+extern crate servo_rand;
 extern crate url;
+extern crate uuid;
+
+pub mod origin;
+
+pub use origin::{OpaqueOrigin, ImmutableOrigin, MutableOrigin};
 
 use std::fmt;
 use std::net::IpAddr;
 use std::ops::{Range, RangeFrom, RangeTo, RangeFull, Index};
 use std::path::Path;
 use std::sync::Arc;
-use url::{Url, Origin, Position};
+use url::{Url, Position};
 
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct ServoUrl(Arc<Url>);
 
 impl ServoUrl {
     pub fn from_url(url: Url) -> Self {
         ServoUrl(Arc::new(url))
@@ -63,18 +70,18 @@ impl ServoUrl {
     pub fn fragment(&self) -> Option<&str> {
         self.0.fragment()
     }
 
     pub fn path(&self) -> &str {
         self.0.path()
     }
 
-    pub fn origin(&self) -> Origin {
-        self.0.origin()
+    pub fn origin(&self) -> ImmutableOrigin {
+        ImmutableOrigin::new(self.0.origin())
     }
 
     pub fn scheme(&self) -> &str {
         self.0.scheme()
     }
 
     pub fn is_secure_scheme(&self) -> bool {
         let scheme = self.scheme();
new file mode 100644
--- /dev/null
+++ b/servo/components/url/origin.rs
@@ -0,0 +1,172 @@
+/* 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 servo_rand;
+use servo_rand::Rng;
+use std::cell::RefCell;
+use std::rc::Rc;
+use url::{Host, Origin};
+#[cfg(feature = "servo")] use url_serde;
+use uuid::Uuid;
+
+/// The origin of an URL
+#[derive(PartialEq, Eq, Clone, Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
+pub enum ImmutableOrigin {
+    /// A globally unique identifier
+    Opaque(OpaqueOrigin),
+
+    /// Consists of the URL's scheme, host and port
+    Tuple(
+        String,
+        #[cfg_attr(feature = "servo",
+                   serde(deserialize_with = "url_serde::deserialize", serialize_with = "url_serde::serialize"))]
+        Host,
+        u16,
+    )
+}
+
+impl ImmutableOrigin {
+    pub fn new(origin: Origin) -> ImmutableOrigin {
+        match origin {
+            Origin::Opaque(_) => ImmutableOrigin::new_opaque(),
+            Origin::Tuple(scheme, host, port) => ImmutableOrigin::Tuple(scheme, host, port),
+        }
+    }
+
+    pub fn same_origin(&self, other: &MutableOrigin) -> bool {
+        self == other.immutable()
+    }
+
+    pub fn same_origin_domain(&self, other: &MutableOrigin) -> bool {
+        !other.has_domain() && self == other.immutable()
+    }
+
+    /// Creates a new opaque origin that is only equal to itself.
+    pub fn new_opaque() -> ImmutableOrigin {
+        ImmutableOrigin::Opaque(OpaqueOrigin(servo_rand::thread_rng().gen()))
+    }
+
+    pub fn scheme(&self) -> Option<&str> {
+        match *self {
+            ImmutableOrigin::Opaque(_) => None,
+            ImmutableOrigin::Tuple(ref scheme, _, _) => Some(&**scheme),
+        }
+    }
+
+    pub fn host(&self) -> Option<&Host> {
+        match *self {
+            ImmutableOrigin::Opaque(_) => None,
+            ImmutableOrigin::Tuple(_, ref host, _) => Some(host),
+        }
+    }
+
+    pub fn port(&self) -> Option<u16> {
+        match *self {
+            ImmutableOrigin::Opaque(_) => None,
+            ImmutableOrigin::Tuple(_, _, port) => Some(port),
+        }
+    }
+
+    pub fn into_url_origin(self) -> Origin {
+        match self {
+            ImmutableOrigin::Opaque(_) => Origin::new_opaque(),
+            ImmutableOrigin::Tuple(scheme, host, port) => Origin::Tuple(scheme, host, port),
+        }
+    }
+
+    /// Return whether this origin is a (scheme, host, port) tuple
+    /// (as opposed to an opaque origin).
+    pub fn is_tuple(&self) -> bool {
+        match *self {
+            ImmutableOrigin::Opaque(..) => false,
+            ImmutableOrigin::Tuple(..) => true,
+        }
+    }
+
+    /// https://html.spec.whatwg.org/multipage/#ascii-serialisation-of-an-origin
+    pub fn ascii_serialization(&self) -> String {
+        self.clone().into_url_origin().ascii_serialization()
+    }
+
+    /// https://html.spec.whatwg.org/multipage/#unicode-serialisation-of-an-origin
+    pub fn unicode_serialization(&self) -> String {
+        self.clone().into_url_origin().unicode_serialization()
+    }
+}
+
+/// Opaque identifier for URLs that have file or other schemes
+#[derive(Eq, PartialEq, Clone, Debug)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+pub struct OpaqueOrigin(Uuid);
+
+#[cfg(feature = "servo")]
+known_heap_size!(0, OpaqueOrigin);
+
+/// A representation of an [origin](https://html.spec.whatwg.org/multipage/#origin-2).
+#[derive(Clone, Debug)]
+pub struct MutableOrigin(Rc<(ImmutableOrigin, RefCell<Option<Host>>)>);
+
+#[cfg(feature = "servo")]
+known_heap_size!(0, MutableOrigin);
+
+impl MutableOrigin {
+    pub fn new(origin: ImmutableOrigin) -> MutableOrigin {
+        MutableOrigin(Rc::new((origin, RefCell::new(None))))
+    }
+
+    pub fn immutable(&self) -> &ImmutableOrigin {
+        &(self.0).0
+    }
+
+    pub fn is_tuple(&self) -> bool {
+        self.immutable().is_tuple()
+    }
+
+    pub fn scheme(&self) -> Option<&str> {
+        self.immutable().scheme()
+    }
+
+    pub fn host(&self) -> Option<&Host> {
+        self.immutable().host()
+    }
+
+    pub fn port(&self) -> Option<u16> {
+        self.immutable().port()
+    }
+
+    pub fn same_origin(&self, other: &MutableOrigin) -> bool {
+        self.immutable() == other.immutable()
+    }
+
+    pub fn same_origin_domain(&self, other: &MutableOrigin) -> bool {
+        if let Some(ref self_domain) = *(self.0).1.borrow() {
+            if let Some(ref other_domain) = *(other.0).1.borrow() {
+                self_domain == other_domain &&
+                    self.immutable().scheme() == other.immutable().scheme()
+            } else {
+                false
+            }
+        } else {
+            self.immutable().same_origin_domain(other)
+        }
+    }
+
+    pub fn domain(&self) -> Option<Host> {
+        (self.0).1.borrow().clone()
+    }
+
+    pub fn set_domain(&self, domain: Host) {
+        *(self.0).1.borrow_mut() = Some(domain);
+    }
+
+    pub fn has_domain(&self) -> bool {
+        (self.0).1.borrow().is_some()
+    }
+
+    pub fn effective_domain(&self) -> Option<Host> {
+        self.immutable().host()
+            .map(|host| self.domain().unwrap_or_else(|| host.clone()))
+    }
+}
--- a/servo/tests/unit/net/fetch.rs
+++ b/servo/tests/unit/net/fetch.rs
@@ -29,26 +29,25 @@ use net::filemanager_thread::FileManager
 use net::hsts::HstsEntry;
 use net::test::HttpState;
 use net_traits::IncludeSubdomains;
 use net_traits::NetworkError;
 use net_traits::ReferrerPolicy;
 use net_traits::request::{Origin, RedirectMode, Referrer, Request, RequestMode};
 use net_traits::response::{CacheState, Response, ResponseBody, ResponseType};
 use servo_config::resource_files::resources_dir_path;
-use servo_url::ServoUrl;
+use servo_url::{ImmutableOrigin, ServoUrl};
 use std::fs::File;
 use std::io::Read;
 use std::rc::Rc;
 use std::sync::{Arc, Mutex};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::mpsc::{Sender, channel};
 use time::{self, Duration};
 use unicase::UniCase;
-use url::Origin as UrlOrigin;
 
 // TODO write a struct that impls Handler for storing test values
 
 #[test]
 fn test_fetch_response_is_not_network_error() {
     static MESSAGE: &'static [u8] = b"";
     let handler = move |_: HyperRequest, response: HyperResponse| {
         response.send(MESSAGE).unwrap();
@@ -218,17 +217,17 @@ fn test_cors_preflight_fetch() {
             response.headers_mut().set(AccessControlAllowOrigin::Any);
             response.send(ACK).unwrap();
         }
     };
     let (mut server, url) = make_server(handler);
 
     let target_url = url.clone().join("a.html").unwrap();
 
-    let origin = Origin::Origin(UrlOrigin::new_opaque());
+    let origin = Origin::Origin(ImmutableOrigin::new_opaque());
     let mut request = Request::new(url.clone(), Some(origin), false, None);
     *request.referrer.borrow_mut() = Referrer::ReferrerUrl(target_url);
     *request.referrer_policy.get_mut() = Some(ReferrerPolicy::Origin);
     request.use_cors_preflight = true;
     request.mode = RequestMode::CorsMode;
     let fetch_response = fetch(request, None);
     let _ = server.close();
 
@@ -255,17 +254,17 @@ fn test_cors_preflight_cache_fetch() {
             response.headers_mut().set(AccessControlMaxAge(6000));
         } else {
             response.headers_mut().set(AccessControlAllowOrigin::Any);
             response.send(ACK).unwrap();
         }
     };
     let (mut server, url) = make_server(handler);
 
-    let origin = Origin::Origin(UrlOrigin::new_opaque());
+    let origin = Origin::Origin(ImmutableOrigin::new_opaque());
     let mut request = Request::new(url.clone(), Some(origin.clone()), false, None);
     *request.referrer.borrow_mut() = Referrer::NoReferrer;
     request.use_cors_preflight = true;
     request.mode = RequestMode::CorsMode;
     let wrapped_request0 = Rc::new(request.clone());
     let wrapped_request1 = Rc::new(request);
 
     let fetch_response0 = fetch_with_cors_cache(wrapped_request0.clone(), &mut cache);
@@ -304,17 +303,17 @@ fn test_cors_preflight_fetch_network_err
             response.headers_mut().set(AccessControlAllowMethods(vec![Method::Get]));
         } else {
             response.headers_mut().set(AccessControlAllowOrigin::Any);
             response.send(ACK).unwrap();
         }
     };
     let (mut server, url) = make_server(handler);
 
-    let origin = Origin::Origin(UrlOrigin::new_opaque());
+    let origin = Origin::Origin(ImmutableOrigin::new_opaque());
     let mut request = Request::new(url, Some(origin), false, None);
     *request.method.borrow_mut() = Method::Extension("CHICKEN".to_owned());
     *request.referrer.borrow_mut() = Referrer::NoReferrer;
     request.use_cors_preflight = true;
     request.mode = RequestMode::CorsMode;
     let fetch_response = fetch(request, None);
     let _ = server.close();
 
@@ -373,17 +372,17 @@ fn test_fetch_response_is_cors_filtered(
             ])
         );
 
         response.send(MESSAGE).unwrap();
     };
     let (mut server, url) = make_server(handler);
 
     // an origin mis-match will stop it from defaulting to a basic filtered response
-    let origin = Origin::Origin(UrlOrigin::new_opaque());
+    let origin = Origin::Origin(ImmutableOrigin::new_opaque());
     let mut request = Request::new(url, Some(origin), false, None);
     *request.referrer.borrow_mut() = Referrer::NoReferrer;
     request.mode = RequestMode::CorsMode;
     let fetch_response = fetch(request, None);
     let _ = server.close();
 
     assert!(!fetch_response.is_network_error());
     assert_eq!(fetch_response.response_type, ResponseType::Cors);
@@ -405,17 +404,17 @@ fn test_fetch_response_is_cors_filtered(
 fn test_fetch_response_is_opaque_filtered() {
     static MESSAGE: &'static [u8] = b"";
     let handler = move |_: HyperRequest, response: HyperResponse| {
         response.send(MESSAGE).unwrap();
     };
     let (mut server, url) = make_server(handler);
 
     // an origin mis-match will fall through to an Opaque filtered response
-    let origin = Origin::Origin(UrlOrigin::new_opaque());
+    let origin = Origin::Origin(ImmutableOrigin::new_opaque());
     let request = Request::new(url, Some(origin), false, None);
     *request.referrer.borrow_mut() = Referrer::NoReferrer;
     let fetch_response = fetch(request, None);
     let _ = server.close();
 
     assert!(!fetch_response.is_network_error());
     assert_eq!(fetch_response.response_type, ResponseType::Opaque);
 
@@ -795,17 +794,17 @@ fn test_fetch_async_returns_complete_res
 fn test_opaque_filtered_fetch_async_returns_complete_response() {
     static MESSAGE: &'static [u8] = b"";
     let handler = move |_: HyperRequest, response: HyperResponse| {
         response.send(MESSAGE).unwrap();
     };
     let (mut server, url) = make_server(handler);
 
     // an origin mis-match will fall through to an Opaque filtered response
-    let origin = Origin::Origin(UrlOrigin::new_opaque());
+    let origin = Origin::Origin(ImmutableOrigin::new_opaque());
     let request = Request::new(url, Some(origin), false, None);
     *request.referrer.borrow_mut() = Referrer::NoReferrer;
 
     let fetch_response = fetch(request, None);
 
     let _ = server.close();
 
     assert_eq!(fetch_response.response_type, ResponseType::Opaque);
--- a/servo/tests/unit/script/origin.rs
+++ b/servo/tests/unit/script/origin.rs
@@ -1,64 +1,63 @@
 /* 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;
-use servo_url::ServoUrl;
+use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
 
 #[test]
 fn same_origin() {
-    let a = Origin::new(&ServoUrl::parse("http://example.com/a.html").unwrap());
-    let b = Origin::new(&ServoUrl::parse("http://example.com/b.html").unwrap());
+    let a = MutableOrigin::new(ServoUrl::parse("http://example.com/a.html").unwrap().origin());
+    let b = MutableOrigin::new(ServoUrl::parse("http://example.com/b.html").unwrap().origin());
     assert!(a.same_origin(&b));
-    assert_eq!(a.is_scheme_host_port_tuple(), true);
+    assert_eq!(a.is_tuple(), true);
 }
 
 #[test]
 fn identical_origin() {
-    let a = Origin::new(&ServoUrl::parse("http://example.com/a.html").unwrap());
+    let a = MutableOrigin::new(ServoUrl::parse("http://example.com/a.html").unwrap().origin());
     assert!(a.same_origin(&a));
 }
 
 #[test]
 fn cross_origin() {
-    let a = Origin::new(&ServoUrl::parse("http://example.com/a.html").unwrap());
-    let b = Origin::new(&ServoUrl::parse("http://example.org/b.html").unwrap());
+    let a = MutableOrigin::new(ServoUrl::parse("http://example.com/a.html").unwrap().origin());
+    let b = MutableOrigin::new(ServoUrl::parse("http://example.org/b.html").unwrap().origin());
     assert!(!a.same_origin(&b));
 }
 
 #[test]
-fn alias_same_origin() {
-    let a = Origin::new(&ServoUrl::parse("http://example.com/a.html").unwrap());
-    let b = Origin::new(&ServoUrl::parse("http://example.com/b.html").unwrap());
-    let c = b.alias();
+fn clone_same_origin() {
+    let a = MutableOrigin::new(ServoUrl::parse("http://example.com/a.html").unwrap().origin());
+    let b = MutableOrigin::new(ServoUrl::parse("http://example.com/b.html").unwrap().origin());
+    let c = b.clone();
     assert!(a.same_origin(&c));
     assert!(b.same_origin(&b));
     assert!(c.same_origin(&b));
-    assert_eq!(c.is_scheme_host_port_tuple(), true);
+    assert_eq!(c.is_tuple(), true);
 }
 
 #[test]
-fn alias_cross_origin() {
-    let a = Origin::new(&ServoUrl::parse("http://example.com/a.html").unwrap());
-    let b = Origin::new(&ServoUrl::parse("http://example.org/b.html").unwrap());
-    let c = b.alias();
+fn clone_cross_origin() {
+    let a = MutableOrigin::new(ServoUrl::parse("http://example.com/a.html").unwrap().origin());
+    let b = MutableOrigin::new(ServoUrl::parse("http://example.org/b.html").unwrap().origin());
+    let c = b.clone();
     assert!(!a.same_origin(&c));
     assert!(b.same_origin(&c));
     assert!(c.same_origin(&c));
 }
 
 #[test]
 fn opaque() {
-    let a = Origin::opaque_identifier();
-    let b = Origin::opaque_identifier();
+    let a = MutableOrigin::new(ImmutableOrigin::new_opaque());
+    let b = MutableOrigin::new(ImmutableOrigin::new_opaque());
     assert!(!a.same_origin(&b));
-    assert_eq!(a.is_scheme_host_port_tuple(), false);
+    assert_eq!(a.is_tuple(), false);
 }
 
 #[test]
 fn opaque_clone() {
-    let a = Origin::opaque_identifier();
-    let b = a.alias();
+    let a = MutableOrigin::new(ImmutableOrigin::new_opaque());
+    let b = a.clone();
     assert!(a.same_origin(&b));
-    assert_eq!(a.is_scheme_host_port_tuple(), false);
+    assert_eq!(a.is_tuple(), false);
 }