Bug 1151899 - Include rust-url-capi (leave-open); r=valentin,ted
authorManish Goregaokar <manishearth@gmail.com>
Wed, 19 Oct 2016 18:37:05 +0530
changeset 362849 54a28d6fbed4db85e0f40ebeaf52d563c8226509
parent 362848 3d88f715c2d232c8b99f41e72f72a53c3b74f427
child 362850 c9a887ab3ba6179894b8e4d2b87c3ebeffa9b14d
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvalentin, ted
bugs1151899
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1151899 - Include rust-url-capi (leave-open); r=valentin,ted MozReview-Commit-ID: 1ITlPLvwzF4
browser/confvars.sh
netwerk/base/nsStandardURL.cpp
netwerk/base/rust-url-capi/.gitignore
netwerk/base/rust-url-capi/Cargo.toml
netwerk/base/rust-url-capi/src/error_mapping.rs
netwerk/base/rust-url-capi/src/lib.rs
netwerk/base/rust-url-capi/src/rust-url-capi.h
netwerk/base/rust-url-capi/src/string_utils.rs
netwerk/base/rust-url-capi/test/Makefile
netwerk/base/rust-url-capi/test/test.cpp
old-configure.in
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
toolkit/library/rust/shared/Cargo.toml
toolkit/library/rust/shared/lib.rs
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -50,14 +50,15 @@ MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3
 ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
 # The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
 MAR_CHANNEL_ID=firefox-mozilla-central
 MOZ_PROFILE_MIGRATOR=1
 MOZ_APP_STATIC_INI=1
 MOZ_WEBGL_CONFORMANT=1
 MOZ_JSDOWNLOADS=1
 MOZ_RUST_MP4PARSE=1
+MOZ_RUST_URLPARSE=1
 
 # Enable checking that add-ons are signed by the trusted root
 MOZ_ADDON_SIGNING=1
 
 # Include the DevTools client, not just the server (which is the default)
 MOZ_DEVTOOLS=all
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -60,16 +60,29 @@ static LazyLogModule gStandardURLLog("ns
     if (!mMutable) { \
         NS_WARNING("attempt to modify an immutable nsStandardURL"); \
         return NS_ERROR_ABORT; \
     } \
   PR_END_MACRO
 
 //----------------------------------------------------------------------------
 
+#ifdef MOZ_RUST_URLPARSE
+extern "C" int32_t c_fn_set_size(void * container, size_t size)
+{
+  ((nsACString *) container)->SetLength(size);
+  return 0;
+}
+
+extern "C" char * c_fn_get_buffer(void * container)
+{
+  return ((nsACString *) container)->BeginWriting();
+}
+#endif
+
 static nsresult
 EncodeString(nsIUnicodeEncoder *encoder, const nsAFlatString &str, nsACString &result)
 {
     nsresult rv;
     int32_t len = str.Length();
     int32_t maxlen;
 
     rv = encoder->GetMaxLength(str.get(), len, &maxlen);
new file mode 100644
--- /dev/null
+++ b/netwerk/base/rust-url-capi/.gitignore
@@ -0,0 +1,2 @@
+/target
+/Cargo.lock
new file mode 100644
--- /dev/null
+++ b/netwerk/base/rust-url-capi/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+
+name = "rust_url_capi"
+version = "0.0.1"
+authors = ["Valentin Gosu <valentin.gosu@gmail.com>"]
+
+[profile.dev]
+opt-level = 3
+debug = true
+rpath = true
+lto = true
+
+[lib]
+name = "rust_url_capi"
+
+
+[dependencies]
+libc = "0.2.0"
+url = "1.2.1"
new file mode 100644
--- /dev/null
+++ b/netwerk/base/rust-url-capi/src/error_mapping.rs
@@ -0,0 +1,68 @@
+use url::ParseError;
+
+pub trait ErrorCode {
+  fn error_code(&self) -> i32;
+}
+
+impl<T: ErrorCode> ErrorCode for Result<(), T> {
+  fn error_code(&self) -> i32 {
+    match *self {
+      Ok(_) => 0,
+      Err(ref error) => error.error_code(),
+    }
+  }
+}
+
+impl ErrorCode for () {
+    fn error_code(&self) -> i32 {
+        return -1;
+    }
+}
+impl ErrorCode for ParseError {
+  fn error_code(&self) -> i32 {
+      return -1;
+//    match *self {
+//      ParseError::EmptyHost                              =>  -1,
+//      ParseError::InvalidScheme                          =>  -2,
+//      ParseError::InvalidPort                            =>  -3,
+//      ParseError::InvalidIpv6Address                     =>  -4,
+//      ParseError::InvalidDomainCharacter                 =>  -5,
+//      ParseError::InvalidCharacter                       =>  -6,
+//      ParseError::InvalidBackslash                       =>  -7,
+//      ParseError::InvalidPercentEncoded                  =>  -8,
+//      ParseError::InvalidAtSymbolInUser                  =>  -9,
+//      ParseError::ExpectedTwoSlashes                     => -10,
+//      ParseError::ExpectedInitialSlash                   => -11,
+//      ParseError::NonUrlCodePoint                        => -12,
+//      ParseError::RelativeUrlWithScheme                  => -13,
+//      ParseError::RelativeUrlWithoutBase                 => -14,
+//      ParseError::RelativeUrlWithNonRelativeBase         => -15,
+//      ParseError::NonAsciiDomainsNotSupportedYet         => -16,
+//      ParseError::CannotSetJavascriptFragment            => -17,
+//      ParseError::CannotSetPortWithFileLikeScheme        => -18,
+//      ParseError::CannotSetUsernameWithNonRelativeScheme => -19,
+//      ParseError::CannotSetPasswordWithNonRelativeScheme => -20,
+//      ParseError::CannotSetHostPortWithNonRelativeScheme => -21,
+//      ParseError::CannotSetHostWithNonRelativeScheme     => -22,
+//      ParseError::CannotSetPortWithNonRelativeScheme     => -23,
+//      ParseError::CannotSetPathWithNonRelativeScheme     => -24,
+//    }
+  }
+}
+
+pub enum NSError {
+  OK,
+  InvalidArg,
+  Failure,
+}
+
+impl ErrorCode for NSError {
+  #[allow(overflowing_literals)]
+  fn error_code(&self) -> i32 {
+    match *self {
+      NSError::OK => 0,
+      NSError::InvalidArg => 0x80070057,
+      NSError::Failure => 0x80004005
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/base/rust-url-capi/src/lib.rs
@@ -0,0 +1,477 @@
+extern crate url;
+use url::{Url, ParseError, ParseOptions};
+use url::quirks;
+extern crate libc;
+use libc::size_t;
+
+
+use std::mem;
+use std::str;
+
+#[allow(non_camel_case_types)]
+pub type rusturl_ptr = *const libc::c_void;
+
+mod string_utils;
+pub use string_utils::*;
+
+mod error_mapping;
+use error_mapping::*;
+
+fn parser<'a>() -> ParseOptions<'a> {
+   Url::options()
+}
+
+fn default_port(scheme: &str) -> Option<u32> {
+    match scheme {
+        "ftp" => Some(21),
+        "gopher" => Some(70),
+        "http" => Some(80),
+        "https" => Some(443),
+        "ws" => Some(80),
+        "wss" => Some(443),
+        "rtsp" => Some(443),
+        "moz-anno" => Some(443),
+        "android" => Some(443),
+        _ => None,
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_new(spec: *mut libc::c_char, len: size_t) -> rusturl_ptr {
+  let slice = std::slice::from_raw_parts(spec as *const libc::c_uchar, len as usize);
+  let url_spec = match str::from_utf8(slice) {
+    Ok(spec) => spec,
+    Err(_) => return 0 as rusturl_ptr
+  };
+
+  let url = match parser().parse(url_spec) {
+    Ok(url) => url,
+    Err(_) => return 0 as rusturl_ptr
+  };
+
+  let url = Box::new(url);
+  Box::into_raw(url) as rusturl_ptr
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_free(urlptr: rusturl_ptr) {
+  if urlptr.is_null() {
+    return ();
+  }
+  let url: Box<Url> = Box::from_raw(urlptr as *mut url::Url);
+  drop(url);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_spec(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url: &Url = mem::transmute(urlptr);
+  cont.assign(&url.to_string())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_scheme(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url: &Url = mem::transmute(urlptr);
+  cont.assign(&url.scheme())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_username(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url: &Url = mem::transmute(urlptr);
+  if url.cannot_be_a_base() {
+      cont.set_size(0)
+  } else {
+      cont.assign(url.username())
+  }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_password(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url: &Url = mem::transmute(urlptr);
+  match url.password() {
+    Some(p) => cont.assign(&p.to_string()),
+    None => cont.set_size(0)
+  }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_host(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url: &Url = mem::transmute(urlptr);
+
+  match url.host() {
+    Some(h) => cont.assign(&h.to_string()),
+    None => cont.set_size(0)
+  }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_port(urlptr: rusturl_ptr) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url: &Url = mem::transmute(urlptr);
+
+  match url.port() {
+    Some(port) => port as i32,
+    None => -1
+  }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_path(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url: &Url = mem::transmute(urlptr);
+  if url.cannot_be_a_base() {
+      cont.set_size(0)
+  } else {
+      cont.assign(url.path())
+  }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_query(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url: &Url = mem::transmute(urlptr);
+  match url.query() {
+    Some(ref s) => cont.assign(s),
+    None => cont.set_size(0)
+  }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_get_fragment(urlptr: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url: &Url = mem::transmute(urlptr);
+
+  match url.fragment() {
+    Some(ref fragment) => cont.assign(fragment),
+    None => cont.set_size(0)
+  }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_has_fragment(urlptr: rusturl_ptr) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url: &Url = mem::transmute(urlptr);
+
+  match url.fragment() {
+    Some(_) => return 1,
+    None => return 0
+  }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_scheme(urlptr: rusturl_ptr, scheme: *mut libc::c_char, len: size_t) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let mut url: &mut Url = mem::transmute(urlptr);
+  let slice = std::slice::from_raw_parts(scheme as *const libc::c_uchar, len as usize);
+
+  let scheme_ = match str::from_utf8(slice).ok() {
+    Some(p) => p,
+    None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+  };
+
+  quirks::set_protocol(url, scheme_).error_code()
+}
+
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_username(urlptr: rusturl_ptr, username: *mut libc::c_char, len: size_t) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let mut url: &mut Url = mem::transmute(urlptr);
+  let slice = std::slice::from_raw_parts(username as *const libc::c_uchar, len as usize);
+
+  let username_ = match str::from_utf8(slice).ok() {
+    Some(p) => p,
+    None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+  };
+
+  quirks::set_username(url, username_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_password(urlptr: rusturl_ptr, password: *mut libc::c_char, len: size_t) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let mut url: &mut Url = mem::transmute(urlptr);
+  let slice = std::slice::from_raw_parts(password as *const libc::c_uchar, len as usize);
+
+  let password_ = match str::from_utf8(slice).ok() {
+    Some(p) => p,
+    None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+  };
+
+  quirks::set_password(url, password_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_host_and_port(urlptr: rusturl_ptr, host_and_port: *mut libc::c_char, len: size_t) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let mut url: &mut Url = mem::transmute(urlptr);
+  let slice = std::slice::from_raw_parts(host_and_port as *const libc::c_uchar, len as usize);
+
+  let host_and_port_ = match str::from_utf8(slice).ok() {
+    Some(p) => p,
+    None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+  };
+
+  quirks::set_host(url, host_and_port_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_host(urlptr: rusturl_ptr, host: *mut libc::c_char, len: size_t) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let mut url: &mut Url = mem::transmute(urlptr);
+  let slice = std::slice::from_raw_parts(host as *const libc::c_uchar, len as usize);
+
+  let hostname = match str::from_utf8(slice).ok() {
+    Some(h) => h,
+    None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+  };
+
+  quirks::set_hostname(url, hostname).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_port(urlptr: rusturl_ptr, port: *mut libc::c_char, len: size_t) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let mut url: &mut Url = mem::transmute(urlptr);
+  let slice = std::slice::from_raw_parts(port as *const libc::c_uchar, len as usize);
+
+  let port_ = match str::from_utf8(slice).ok() {
+    Some(p) => p,
+    None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+  };
+
+  quirks::set_port(url, port_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_port_no(urlptr: rusturl_ptr, new_port: i32) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let mut url: &mut Url = mem::transmute(urlptr);
+  if url.cannot_be_a_base() {
+      -100
+  } else {
+    if url.scheme() == "file" {
+        return -100;
+    }
+    match default_port(url.scheme()) {
+      Some(def_port) => if new_port == def_port as i32 {
+        let _ = url.set_port(None);
+        return NSError::OK.error_code();
+      },
+      None => {}
+    };
+    if new_port > std::u16::MAX as i32 || new_port < 0 {
+      let _ = url.set_port(None);
+    } else {
+      let _ = url.set_port(Some(new_port as u16));
+    }
+    NSError::OK.error_code()
+  }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_path(urlptr: rusturl_ptr, path: *mut libc::c_char, len: size_t) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let mut url: &mut Url = mem::transmute(urlptr);
+  let slice = std::slice::from_raw_parts(path as *const libc::c_uchar, len as usize);
+
+  let path_ = match str::from_utf8(slice).ok() {
+    Some(p) => p,
+    None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+  };
+
+  quirks::set_pathname(url, path_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_query(urlptr: rusturl_ptr, query: *mut libc::c_char, len: size_t) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let mut url: &mut Url = mem::transmute(urlptr);
+  let slice = std::slice::from_raw_parts(query as *const libc::c_uchar, len as usize);
+
+  let query_ = match str::from_utf8(slice).ok() {
+    Some(p) => p,
+    None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+  };
+
+  quirks::set_search(url, query_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_set_fragment(urlptr: rusturl_ptr, fragment: *mut libc::c_char, len: size_t) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let mut url: &mut Url = mem::transmute(urlptr);
+  let slice = std::slice::from_raw_parts(fragment as *const libc::c_uchar, len as usize);
+
+  let fragment_ = match str::from_utf8(slice).ok() {
+    Some(p) => p,
+    None => return ParseError::InvalidDomainCharacter.error_code() // utf-8 failed
+  };
+
+  quirks::set_hash(url, fragment_).error_code()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_resolve(urlptr: rusturl_ptr, resolve: *mut libc::c_char, len: size_t, cont: *mut libc::c_void) -> i32 {
+  if urlptr.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url: &mut Url = mem::transmute(urlptr);
+
+    let slice = std::slice::from_raw_parts(resolve as *const libc::c_uchar, len as usize);
+
+  let resolve_ = match str::from_utf8(slice).ok() {
+    Some(p) => p,
+    None => return NSError::Failure.error_code()
+  };
+
+  match parser().base_url(Some(&url)).parse(resolve_).ok() {
+    Some(u) => cont.assign(&u.to_string()),
+    None => cont.set_size(0)
+  }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_common_base_spec(urlptr1: rusturl_ptr, urlptr2: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+  if urlptr1.is_null() || urlptr2.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url1: &Url = mem::transmute(urlptr1);
+  let url2: &Url = mem::transmute(urlptr2);
+
+  if url1 == url2 {
+    return cont.assign(&url1.to_string());
+  }
+
+  if url1.scheme() != url2.scheme() ||
+     url1.host() != url2.host() ||
+     url1.username() != url2.username() ||
+     url1.password() != url2.password() ||
+     url1.port() != url2.port() {
+    return cont.set_size(0);
+  }
+
+  let path1 = match url1.path_segments() {
+    Some(path) => path,
+    None => return cont.set_size(0)
+  };
+  let path2 = match url2.path_segments() {
+    Some(path) => path,
+    None => return cont.set_size(0)
+  };
+
+  let mut url = url1.clone();
+  url.set_query(None);
+  let _ = url.set_host(None);
+  {
+    let mut new_segments = if let Ok(segments) = url.path_segments_mut() {
+      segments
+    } else {
+      return cont.set_size(0)
+    };
+
+    for (p1, p2) in path1.zip(path2) {
+      if p1 != p2 {
+        break;
+      } else {
+          new_segments.push(p1);
+      }
+    }
+  }
+
+  cont.assign(&url.to_string())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rusturl_relative_spec(urlptr1: rusturl_ptr, urlptr2: rusturl_ptr, cont: *mut libc::c_void) -> i32 {
+  if urlptr1.is_null() || urlptr2.is_null() {
+    return NSError::InvalidArg.error_code();
+  }
+  let url1: &Url = mem::transmute(urlptr1);
+  let url2: &Url = mem::transmute(urlptr2);
+
+  if url1 == url2 {
+    return cont.set_size(0);
+  }
+
+  if url1.scheme() != url2.scheme() ||
+     url1.host() != url2.host() ||
+     url1.username() != url2.username() ||
+     url1.password() != url2.password() ||
+     url1.port() != url2.port() {
+    return cont.assign(&url2.to_string());
+  }
+
+  let mut path1 = match url1.path_segments() {
+    Some(path) => path,
+    None => return cont.assign(&url2.to_string())
+  };
+  let mut path2 = match url2.path_segments() {
+    Some(path) => path,
+    None => return cont.assign(&url2.to_string())
+  };
+
+  // TODO: file:// on WIN?
+
+  // Exhaust the part of the iterators that match
+  while let (Some(ref p1), Some(ref p2)) = (path1.next(), path2.next()) {
+    if p1 != p2 {
+      break;
+    }
+  }
+
+  let mut buffer: String = "".to_string();
+  for _ in path1 {
+    buffer = buffer + "../";
+  }
+  for p2 in path2 {
+    buffer = buffer + p2 + "/";
+  }
+
+  return cont.assign(&buffer);
+}
+
new file mode 100644
--- /dev/null
+++ b/netwerk/base/rust-url-capi/src/rust-url-capi.h
@@ -0,0 +1,45 @@
+#ifndef __RUST_URL_CAPI
+#define __RUST_URL_CAPI
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rusturl;
+typedef struct rusturl* rusturl_ptr;
+
+rusturl_ptr rusturl_new(const char *spec, size_t src_len);
+void rusturl_free(rusturl_ptr url);
+
+int32_t rusturl_get_spec(rusturl_ptr url, void*);
+int32_t rusturl_get_scheme(rusturl_ptr url, void*);
+int32_t rusturl_get_username(rusturl_ptr url, void*);
+int32_t rusturl_get_password(rusturl_ptr url, void*);
+int32_t rusturl_get_host(rusturl_ptr url, void*);
+int32_t rusturl_get_port(rusturl_ptr url); // returns port or -1
+int32_t rusturl_get_path(rusturl_ptr url, void*);
+int32_t rusturl_get_query(rusturl_ptr url, void*);
+int32_t rusturl_get_fragment(rusturl_ptr url, void*);
+int32_t rusturl_has_fragment(rusturl_ptr url); // 1 true, 0 false, < 0 error
+
+int32_t rusturl_set_scheme(rusturl_ptr url, const char *scheme, size_t len);
+int32_t rusturl_set_username(rusturl_ptr url, const char *user, size_t len);
+int32_t rusturl_set_password(rusturl_ptr url, const char *pass, size_t len);
+int32_t rusturl_set_host_and_port(rusturl_ptr url, const char *hostport, size_t len);
+int32_t rusturl_set_host(rusturl_ptr url, const char *host, size_t len);
+int32_t rusturl_set_port(rusturl_ptr url, const char *port, size_t len);
+int32_t rusturl_set_port_no(rusturl_ptr url, const int32_t port);
+int32_t rusturl_set_path(rusturl_ptr url, const char *path, size_t len);
+int32_t rusturl_set_query(rusturl_ptr url, const char *path, size_t len);
+int32_t rusturl_set_fragment(rusturl_ptr url, const char *path, size_t len);
+
+int32_t rusturl_resolve(rusturl_ptr url, const char *relative, size_t len, void*);
+int32_t rusturl_common_base_spec(rusturl_ptr url1, rusturl_ptr url2, void*);
+int32_t rusturl_relative_spec(rusturl_ptr url1, rusturl_ptr url2, void*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __RUST_URL_CAPI
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/netwerk/base/rust-url-capi/src/string_utils.rs
@@ -0,0 +1,57 @@
+extern crate libc;
+use libc::size_t;
+
+extern crate std;
+use std::ptr;
+
+use error_mapping::*;
+
+extern "C" {
+  fn c_fn_set_size(user: *mut libc::c_void, size: size_t) -> i32;
+  fn c_fn_get_buffer(user: *mut libc::c_void) -> *mut libc::c_char;
+}
+
+pub trait StringContainer {
+  fn set_size(&self, size_t) -> i32;
+  fn get_buffer(&self) -> *mut libc::c_char;
+  fn assign(&self, content: &str) -> i32;
+}
+
+impl StringContainer for *mut libc::c_void {
+  fn set_size(&self, size: size_t) -> i32 {
+    if (*self).is_null() {
+      return NSError::InvalidArg.error_code();
+    }
+    unsafe {
+      c_fn_set_size(*self, size);
+    }
+
+    return NSError::OK.error_code();
+  }
+  fn get_buffer(&self) -> *mut libc::c_char {
+    if (*self).is_null() {
+      return 0 as *mut libc::c_char;
+    }
+    unsafe {
+      c_fn_get_buffer(*self)
+    }
+  }
+  fn assign(&self, content: &str) -> i32 {
+    if (*self).is_null() {
+      return NSError::InvalidArg.error_code();
+    }
+
+    unsafe {
+      let slice = content.as_bytes();
+      c_fn_set_size(*self, slice.len());
+      let buf = c_fn_get_buffer(*self);
+      if buf.is_null() {
+        return NSError::Failure.error_code();
+      }
+
+      ptr::copy(slice.as_ptr(), buf as *mut u8, slice.len());
+    }
+
+    NSError::OK.error_code()
+  }
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/base/rust-url-capi/test/Makefile
@@ -0,0 +1,4 @@
+all:
+	cd .. && cargo build
+	g++ -Wall -o test test.cpp ../target/debug/librust*.a -ldl -lpthread -lrt -lgcc_s -lpthread -lc -lm -std=c++0x
+	./test
new file mode 100644
--- /dev/null
+++ b/netwerk/base/rust-url-capi/test/test.cpp
@@ -0,0 +1,141 @@
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "../src/rust-url-capi.h"
+
+class StringContainer
+{
+public:
+  StringContainer()
+  {
+    mBuffer = nullptr;
+    mLength = 0;
+  }
+
+  ~StringContainer()
+  {
+    free(mBuffer);
+    mBuffer = nullptr;
+  }
+
+  void SetSize(size_t size)
+  {
+    mLength = size;
+    if (mBuffer) {
+      mBuffer = (char *)realloc(mBuffer, size);
+      return;
+    }
+    mBuffer = (char *)malloc(size);
+  }
+
+  char * GetBuffer()
+  {
+    return mBuffer;
+  }
+
+  void CheckEquals(const char * ref) {
+    int32_t refLen = strlen(ref);
+    printf("CheckEquals: %s (len:%d)\n", ref, refLen);
+    if (refLen != mLength || strncmp(mBuffer, ref, mLength)) {
+      printf("\t--- ERROR ---\n");
+      printf("Got        : ");
+      fwrite(mBuffer, mLength, 1, stdout);
+      printf(" (len:%d)\n", mLength);
+      exit(-1);
+    }
+    printf("-> OK\n");
+  }
+private:
+  int32_t mLength;
+  char * mBuffer;
+};
+
+extern "C" int32_t c_fn_set_size(void * container, size_t size)
+{
+  ((StringContainer *) container)->SetSize(size);
+  return 0;
+}
+
+extern "C" char * c_fn_get_buffer(void * container)
+{
+  return ((StringContainer *) container)->GetBuffer();
+}
+
+#define TEST_CALL(func, expected)                  \
+{                                                  \
+  int32_t code = func;                             \
+  printf("%s -> code %d\n", #func, code);          \
+  assert(code == expected);                        \
+  printf("-> OK\n");                               \
+}                                                  \
+
+
+int main() {
+  // Create URL
+  rusturl_ptr url = rusturl_new("http://example.com/path/some/file.txt",
+                                strlen("http://example.com/path/some/file.txt"));
+  assert(url); // Check we have a URL
+
+  StringContainer container;
+
+  TEST_CALL(rusturl_get_spec(url, &container), 0);
+  container.CheckEquals("http://example.com/path/some/file.txt");
+  TEST_CALL(rusturl_set_host(url, "test.com", strlen("test.com")), 0);
+  TEST_CALL(rusturl_get_host(url, &container), 0);
+  container.CheckEquals("test.com");
+  TEST_CALL(rusturl_get_path(url, &container), 0);
+  container.CheckEquals("/path/some/file.txt");
+  TEST_CALL(rusturl_set_path(url, "hello/../else.txt", strlen("hello/../else.txt")), 0);
+  TEST_CALL(rusturl_get_path(url, &container), 0);
+  container.CheckEquals("/else.txt");
+  TEST_CALL(rusturl_resolve(url, "./bla/file.txt", strlen("./bla/file.txt"), &container), 0);
+  container.CheckEquals("http://test.com/bla/file.txt");
+  TEST_CALL(rusturl_get_scheme(url, &container), 0);
+  container.CheckEquals("http");
+  TEST_CALL(rusturl_set_username(url, "user", strlen("user")), 0);
+  TEST_CALL(rusturl_get_username(url, &container), 0);
+  container.CheckEquals("user");
+  TEST_CALL(rusturl_get_spec(url, &container), 0);
+  container.CheckEquals("http://user@test.com/else.txt");
+  TEST_CALL(rusturl_set_password(url, "pass", strlen("pass")), 0);
+  TEST_CALL(rusturl_get_password(url, &container), 0);
+  container.CheckEquals("pass");
+  TEST_CALL(rusturl_get_spec(url, &container), 0);
+  container.CheckEquals("http://user:pass@test.com/else.txt");
+  TEST_CALL(rusturl_set_username(url, "", strlen("")), 0);
+  TEST_CALL(rusturl_set_password(url, "", strlen("")), 0);
+  TEST_CALL(rusturl_get_spec(url, &container), 0);
+  container.CheckEquals("http://test.com/else.txt");
+  TEST_CALL(rusturl_set_host_and_port(url, "example.org:1234", strlen("example.org:1234")), 0);
+  TEST_CALL(rusturl_get_host(url, &container), 0);
+  container.CheckEquals("example.org");
+  assert(rusturl_get_port(url) == 1234);
+  TEST_CALL(rusturl_set_port(url, "9090", strlen("9090")), 0);
+  assert(rusturl_get_port(url) == 9090);
+  TEST_CALL(rusturl_set_query(url, "x=1", strlen("x=1")), 0);
+  TEST_CALL(rusturl_get_query(url, &container), 0);
+  container.CheckEquals("x=1");
+  TEST_CALL(rusturl_set_fragment(url, "fragment", strlen("fragment")), 0);
+  TEST_CALL(rusturl_get_fragment(url, &container), 0);
+  container.CheckEquals("fragment");
+  TEST_CALL(rusturl_get_spec(url, &container), 0);
+  container.CheckEquals("http://example.org:9090/else.txt?x=1#fragment");
+
+  // Free the URL
+  rusturl_free(url);
+
+  url = rusturl_new("http://example.com/#",
+                                strlen("http://example.com/#"));
+  assert(url); // Check we have a URL
+
+  assert(rusturl_has_fragment(url) == 1);
+  TEST_CALL(rusturl_set_fragment(url, "", 0), 0);
+  assert(rusturl_has_fragment(url) == 0);
+  TEST_CALL(rusturl_get_spec(url, &container), 0);
+  container.CheckEquals("http://example.com/");
+
+  rusturl_free(url);
+
+  printf("SUCCESS\n");
+  return 0;
+}
\ No newline at end of file
--- a/old-configure.in
+++ b/old-configure.in
@@ -2443,16 +2443,19 @@ if test -n "$MOZ_MULET"; then
     AC_DEFINE(MOZ_MULET)
 fi
 
 # Propagate feature switches for code written in rust from confvars.sh
 if test -n "$MOZ_RUST"; then
     if test -n "$MOZ_RUST_MP4PARSE"; then
         AC_DEFINE(MOZ_RUST_MP4PARSE)
     fi
+    if test -n "$MOZ_RUST_URLPARSE"; then
+        AC_DEFINE(MOZ_RUST_URLPARSE)
+    fi
 fi
 
 AC_SUBST(MOZ_PHOENIX)
 AC_SUBST(MOZ_XULRUNNER)
 AC_SUBST(MOZ_B2G)
 AC_SUBST(MOZ_MULET)
 AC_SUBST(MOZ_B2G_VERSION)
 
--- a/toolkit/library/gtest/rust/Cargo.lock
+++ b/toolkit/library/gtest/rust/Cargo.lock
@@ -13,19 +13,40 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "gkrust-shared"
 version = "0.1.0"
 dependencies = [
  "mp4parse_capi 0.5.1",
  "nsstring 0.1.0",
+ "rust_url_capi 0.0.1",
 ]
 
 [[package]]
+name = "idna"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "matches"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "mp4parse"
 version = "0.5.1"
 dependencies = [
  "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp4parse-gtest"
@@ -44,10 +65,46 @@ version = "0.1.0"
 
 [[package]]
 name = "nsstring-gtest"
 version = "0.1.0"
 dependencies = [
  "nsstring 0.1.0",
 ]
 
+[[package]]
+name = "rust_url_capi"
+version = "0.0.1"
+dependencies = [
+ "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "url"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [metadata]
 "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
+"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
+"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d"
+"checksum matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc3ad8109fa4b522f9b0cd81440422781f564aaf8c195de6b9d6642177ad0dd"
+"checksum unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c1f7ceb96afdfeedee42bade65a0d585a6a0106f681b6749c8ff4daa8df30b3f"
+"checksum unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26643a2f83bac55f1976fb716c10234485f9202dcd65cfbdf9da49867b271172"
+"checksum url 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8527c62d9869a08325c38272b3f85668df22a65890c61a639d233dc0ed0b23a2"
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -11,19 +11,40 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "gkrust-shared"
 version = "0.1.0"
 dependencies = [
  "mp4parse_capi 0.5.1",
  "nsstring 0.1.0",
+ "rust_url_capi 0.0.1",
 ]
 
 [[package]]
+name = "idna"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "matches"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "mp4parse"
 version = "0.5.1"
 dependencies = [
  "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp4parse_capi"
@@ -31,10 +52,46 @@ version = "0.5.1"
 dependencies = [
  "mp4parse 0.5.1",
 ]
 
 [[package]]
 name = "nsstring"
 version = "0.1.0"
 
+[[package]]
+name = "rust_url_capi"
+version = "0.0.1"
+dependencies = [
+ "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "url"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [metadata]
 "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
+"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
+"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d"
+"checksum matches 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc3ad8109fa4b522f9b0cd81440422781f564aaf8c195de6b9d6642177ad0dd"
+"checksum unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c1f7ceb96afdfeedee42bade65a0d585a6a0106f681b6749c8ff4daa8df30b3f"
+"checksum unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26643a2f83bac55f1976fb716c10234485f9202dcd65cfbdf9da49867b271172"
+"checksum url 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8527c62d9869a08325c38272b3f85668df22a65890c61a639d233dc0ed0b23a2"
--- a/toolkit/library/rust/shared/Cargo.toml
+++ b/toolkit/library/rust/shared/Cargo.toml
@@ -3,16 +3,17 @@ name = "gkrust-shared"
 version = "0.1.0"
 authors = ["nobody@mozilla.org"]
 license = "MPL-2.0"
 description = "Shared Rust code for libxul"
 
 [dependencies]
 mp4parse_capi = { path = "../../../../media/libstagefright/binding/mp4parse_capi" }
 nsstring = { path = "../../../../xpcom/rust/nsstring" }
+rust_url_capi = { path = "../../../../netwerk/base/rust-url-capi" }
 
 [lib]
 path = "lib.rs"
 test = false
 doctest = false
 bench = false
 doc = false
 plugin = false
--- a/toolkit/library/rust/shared/lib.rs
+++ b/toolkit/library/rust/shared/lib.rs
@@ -1,6 +1,7 @@
 // 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/.
 
 extern crate mp4parse_capi;
 extern crate nsstring;
+extern crate rust_url_capi;