Bug 1407540 - Allow MozURL to be constructed with a base URL r=mayhemer
authorValentin Gosu <valentin.gosu@gmail.com>
Wed, 11 Oct 2017 12:18:28 +0200
changeset 679090 bab0d9666afb77ce8a924cb995aab9062f79a712
parent 679089 08298837c5e57aac61f8a4093468bb755ffef62b
child 679091 dd04bc03bb5ef42e178e9ecaeb202f78281f0f26
push id84141
push userbmo:schien@mozilla.com
push dateThu, 12 Oct 2017 11:13:04 +0000
reviewersmayhemer
bugs1407540
milestone58.0a1
Bug 1407540 - Allow MozURL to be constructed with a base URL r=mayhemer MozReview-Commit-ID: Fg2aDJPhwQO
netwerk/base/MozURL.cpp
netwerk/base/MozURL.h
netwerk/base/RustURL.cpp
netwerk/base/rust-url-capi/src/lib.rs
netwerk/base/rust-url-capi/src/rust-url-capi.h
netwerk/test/gtest/TestMozURL.cpp
--- a/netwerk/base/MozURL.cpp
+++ b/netwerk/base/MozURL.cpp
@@ -6,19 +6,20 @@
 
 namespace mozilla {
 namespace net {
 
 NS_IMPL_ADDREF(MozURL)
 NS_IMPL_RELEASE(MozURL)
 
 /* static */ nsresult
-MozURL::Init(const nsACString& aSpec, MozURL** aURL)
+MozURL::Init(MozURL** aURL, const nsACString& aSpec, const MozURL* aBaseURL)
 {
-  rusturl* ptr = rusturl_new(&aSpec);
+  rusturl* base = aBaseURL ? aBaseURL->mURL.get() : nullptr;
+  rusturl* ptr = rusturl_new(&aSpec, base);
   if (!ptr) {
     return NS_ERROR_FAILURE;
   }
   RefPtr<MozURL> url = new MozURL(ptr);
   url.forget(aURL);
   return NS_OK;
 }
 
--- a/netwerk/base/MozURL.h
+++ b/netwerk/base/MozURL.h
@@ -13,32 +13,33 @@ namespace net {
 
 // This class provides a thread-safe, immutable URL parser.
 // As long as there is RefPtr to the object, you may use it on any thread.
 // The constructor is private. One can instantiate the object by
 // calling the Init() method as such:
 //
 // RefPtr<MozURL> url;
 // nsAutoCString href("http://example.com/path?query#ref");
-// nsresult rv = MozURL::Init(href, getter_AddRefs(url));
+// nsresult rv = MozURL::Init(getter_AddRefs(url), href);
 // if (NS_SUCCEEDED(rv)) { /* use url */ }
 //
 // When changing the URL is needed, you need to call the Mutate() method.
 // This gives you a Mutator object, on which you can perform setter operations.
 // Calling Finalize() on the Mutator will result in a new MozURL and a status
 // code. If any of the setter operations failed, it will be reflected in the
 // status code, and a null MozURL.
 //
 // Note: In the case of a domain name containing non-ascii characters,
 // GetSpec and GetHostname will return the IDNA(punycode) version of the host.
 // Also note that for now, MozURL only supports the UTF-8 charset.
 class MozURL final
 {
 public:
-  static nsresult Init(const nsACString& aSpec, MozURL** aURL);
+  static nsresult Init(MozURL** aURL, const nsACString& aSpec,
+                       const MozURL* aBaseURL = nullptr);
 
   nsresult GetScheme(nsACString& aScheme);
   nsresult GetSpec(nsACString& aSpec);
   nsresult GetUsername(nsACString& aUser);
   nsresult GetPassword(nsACString& aPassword);
   // Will return the hostname of URL. If the hostname is an IPv6 address,
   // it will be enclosed in square brackets, such as `[::1]`
   nsresult GetHostname(nsACString& aHost);
--- a/netwerk/base/RustURL.cpp
+++ b/netwerk/base/RustURL.cpp
@@ -53,17 +53,17 @@ RustURL::GetSpec(nsACString & aSpec)
   return rusturl_get_spec(mURL.get(), &aSpec);
 }
 
 NS_IMETHODIMP
 RustURL::SetSpec(const nsACString & aSpec)
 {
   ENSURE_MUTABLE();
 
-  rusturl* ptr = rusturl_new(&aSpec);
+  rusturl* ptr = rusturl_new(&aSpec, nullptr);
   if (!ptr) {
     return NS_ERROR_FAILURE;
   }
   mURL.reset(ptr);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/rust-url-capi/src/lib.rs
+++ b/netwerk/base/rust-url-capi/src/lib.rs
@@ -35,22 +35,29 @@ fn default_port(scheme: &str) -> Option<
         "rtsp" => Some(443),
         "moz-anno" => Some(443),
         "android" => Some(443),
         _ => None,
     }
 }
 
 #[no_mangle]
-pub extern "C" fn rusturl_new(spec: &nsACString) -> *mut Url {
+pub extern "C" fn rusturl_new(spec: &nsACString, baseptr: Option<&Url>) -> *mut Url {
   let url_spec = match str::from_utf8(spec) {
     Ok(spec) => spec,
     Err(_) => return ptr::null_mut(),
   };
 
+  if let Some(base) = baseptr {
+    match base.join(url_spec) {
+         Ok(url) => return Box::into_raw(Box::new(url)),
+         Err(_) => return ptr::null_mut()
+    };
+  }
+
   match parser().parse(url_spec) {
     Ok(url) => Box::into_raw(Box::new(url)),
     Err(_) => return ptr::null_mut(),
   }
 }
 
 #[no_mangle]
 pub extern "C" fn rusturl_clone(urlptr: Option<&Url>) -> *mut Url {
--- a/netwerk/base/rust-url-capi/src/rust-url-capi.h
+++ b/netwerk/base/rust-url-capi/src/rust-url-capi.h
@@ -14,17 +14,17 @@ extern "C" {
 // * The int32_t* and bool* outparameter pointer is unchecked, and must
 //   be non-null.
 // * All rusturl* pointers must refer to pointers which are returned
 //   by rusturl_new, and must be freed with rusturl_free.
 
 // The `rusturl` opaque type is equivalent to the rust type `::url::Url`
 struct rusturl;
 
-rusturl* rusturl_new(const nsACString* spec);
+rusturl* rusturl_new(const nsACString* spec, const rusturl* base);
 rusturl* rusturl_clone(const rusturl* url);
 /* unsafe */ void rusturl_free(rusturl* url);
 
 nsresult rusturl_get_spec(const rusturl* url, nsACString* cont);
 nsresult rusturl_get_scheme(const rusturl* url, nsACString* cont);
 nsresult rusturl_get_username(const rusturl* url, nsACString* cont);
 nsresult rusturl_get_password(const rusturl* url, nsACString* cont);
 nsresult rusturl_get_host(const rusturl* url, nsACString* cont);
--- a/netwerk/test/gtest/TestMozURL.cpp
+++ b/netwerk/test/gtest/TestMozURL.cpp
@@ -5,17 +5,17 @@
 #include "../../base/MozURL.h"
 
 using namespace mozilla::net;
 
 TEST(TestMozURL, Getters)
 {
   nsAutoCString href("http://user:pass@example.com/path?query#ref");
   RefPtr<MozURL> url;
-  ASSERT_EQ(MozURL::Init(href, getter_AddRefs(url)), NS_OK);
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
 
   nsAutoCString out;
 
   ASSERT_EQ(url->GetScheme(out), NS_OK);
   ASSERT_TRUE(out.EqualsLiteral("http"));
 
   ASSERT_EQ(url->GetSpec(out), NS_OK);
   ASSERT_TRUE(out == href);
@@ -34,26 +34,26 @@ TEST(TestMozURL, Getters)
 
   ASSERT_EQ(url->GetQuery(out), NS_OK);
   ASSERT_TRUE(out.EqualsLiteral("query"));
 
   ASSERT_EQ(url->GetRef(out), NS_OK);
   ASSERT_TRUE(out.EqualsLiteral("ref"));
 
   url = nullptr;
-  ASSERT_EQ(MozURL::Init(NS_LITERAL_CSTRING(""), getter_AddRefs(url)),
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url), NS_LITERAL_CSTRING("")),
             NS_ERROR_FAILURE);
   ASSERT_EQ(url, nullptr);
 }
 
 TEST(TestMozURL, MutatorChain)
 {
   nsAutoCString href("http://user:pass@example.com/path?query#ref");
   RefPtr<MozURL> url;
-  ASSERT_EQ(MozURL::Init(href, getter_AddRefs(url)), NS_OK);
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
   nsAutoCString out;
 
   RefPtr<MozURL> url2;
   ASSERT_EQ(url->Mutate().SetScheme(NS_LITERAL_CSTRING("https"))
                          .SetUsername(NS_LITERAL_CSTRING("newuser"))
                          .SetPassword(NS_LITERAL_CSTRING("newpass"))
                          .SetHostname(NS_LITERAL_CSTRING("test"))
                          .SetFilePath(NS_LITERAL_CSTRING("new/file/path"))
@@ -64,17 +64,17 @@ TEST(TestMozURL, MutatorChain)
   ASSERT_EQ(url2->GetSpec(out), NS_OK);
   ASSERT_TRUE(out.EqualsLiteral("https://newuser:newpass@test/new/file/path?bla#huh"));
 }
 
 TEST(TestMozURL, MutatorFinalizeTwice)
 {
   nsAutoCString href("http://user:pass@example.com/path?query#ref");
   RefPtr<MozURL> url;
-  ASSERT_EQ(MozURL::Init(href, getter_AddRefs(url)), NS_OK);
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
   nsAutoCString out;
 
   RefPtr<MozURL> url2;
   MozURL::Mutator mut = url->Mutate();
   mut.SetScheme(NS_LITERAL_CSTRING("https")); // Change the scheme to https
   ASSERT_EQ(mut.Finalize(getter_AddRefs(url2)), NS_OK);
   ASSERT_EQ(url2->GetSpec(out), NS_OK);
   ASSERT_TRUE(out.EqualsLiteral("https://user:pass@example.com/path?query#ref"));
@@ -84,20 +84,38 @@ TEST(TestMozURL, MutatorFinalizeTwice)
   ASSERT_EQ(mut.Finalize(getter_AddRefs(url2)), NS_ERROR_NOT_AVAILABLE);
   ASSERT_EQ(url2, nullptr);
 }
 
 TEST(TestMozURL, MutatorErrorStatus)
 {
   nsAutoCString href("http://user:pass@example.com/path?query#ref");
   RefPtr<MozURL> url;
-  ASSERT_EQ(MozURL::Init(href, getter_AddRefs(url)), NS_OK);
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
   nsAutoCString out;
 
   // Test that trying to set the scheme to a bad value will get you an error
   MozURL::Mutator mut = url->Mutate();
   mut.SetScheme(NS_LITERAL_CSTRING("!@#$%^&*("));
   ASSERT_EQ(mut.GetStatus(), NS_ERROR_MALFORMED_URI);
 
   // Test that the mutator will not work after one faulty operation
   mut.SetScheme(NS_LITERAL_CSTRING("test"));
   ASSERT_EQ(mut.GetStatus(), NS_ERROR_MALFORMED_URI);
 }
+
+TEST(TestMozURL, InitWithBase)
+{
+  nsAutoCString href("https://example.net/a/b.html");
+  RefPtr<MozURL> url;
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
+  nsAutoCString out;
+
+  ASSERT_EQ(url->GetSpec(out), NS_OK);
+  ASSERT_TRUE(out.EqualsLiteral("https://example.net/a/b.html"));
+
+  RefPtr<MozURL> url2;
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url2), NS_LITERAL_CSTRING("c.png"),
+                         url), NS_OK);
+
+  ASSERT_EQ(url2->GetSpec(out), NS_OK);
+  ASSERT_TRUE(out.EqualsLiteral("https://example.net/a/c.png"));
+}