summaryrefslogtreecommitdiffstats
path: root/netwerk/base/mozurl
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--netwerk/base/mozurl/.gitignore2
-rw-r--r--netwerk/base/mozurl/Cargo.toml12
-rw-r--r--netwerk/base/mozurl/MozURL.cpp12
-rw-r--r--netwerk/base/mozurl/MozURL.h233
-rw-r--r--netwerk/base/mozurl/cbindgen.toml61
-rw-r--r--netwerk/base/mozurl/moz.build22
-rw-r--r--netwerk/base/mozurl/src/lib.rs574
7 files changed, 916 insertions, 0 deletions
diff --git a/netwerk/base/mozurl/.gitignore b/netwerk/base/mozurl/.gitignore
new file mode 100644
index 0000000000..4fffb2f89c
--- /dev/null
+++ b/netwerk/base/mozurl/.gitignore
@@ -0,0 +1,2 @@
+/target
+/Cargo.lock
diff --git a/netwerk/base/mozurl/Cargo.toml b/netwerk/base/mozurl/Cargo.toml
new file mode 100644
index 0000000000..1166bd2bfa
--- /dev/null
+++ b/netwerk/base/mozurl/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "mozurl"
+version = "0.0.1"
+authors = ["Nika Layzell <nika@thelayzells.com>"]
+license = "MPL-2.0"
+
+[dependencies]
+url = "2.4"
+nserror = { path = "../../../xpcom/rust/nserror" }
+nsstring = { path = "../../../xpcom/rust/nsstring" }
+xpcom = { path = "../../../xpcom/rust/xpcom" }
+uuid = { version = "1.0", features = ["v4"] }
diff --git a/netwerk/base/mozurl/MozURL.cpp b/netwerk/base/mozurl/MozURL.cpp
new file mode 100644
index 0000000000..47a679164a
--- /dev/null
+++ b/netwerk/base/mozurl/MozURL.cpp
@@ -0,0 +1,12 @@
+/* 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/. */
+
+#include "mozilla/StaticPrefs_security.h"
+
+extern "C" {
+
+bool Gecko_StrictFileOriginPolicy() {
+ return mozilla::StaticPrefs::security_fileuri_strict_origin_policy();
+}
+}
diff --git a/netwerk/base/mozurl/MozURL.h b/netwerk/base/mozurl/MozURL.h
new file mode 100644
index 0000000000..60e8c189ae
--- /dev/null
+++ b/netwerk/base/mozurl/MozURL.h
@@ -0,0 +1,233 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozURL_h__
+#define mozURL_h__
+
+#include "mozilla/net/MozURL_ffi.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Result.h"
+
+namespace mozilla {
+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(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.
+//
+// Implementor Note: This type is only a holder for methods in C++, and does not
+// reflect the actual layout of the type.
+class MozURL final {
+ public:
+ static nsresult Init(MozURL** aURL, const nsACString& aSpec,
+ const MozURL* aBaseURL = nullptr) {
+ return mozurl_new(aURL, &aSpec, aBaseURL);
+ }
+
+ nsDependentCSubstring Spec() const { return mozurl_spec(this); }
+ nsDependentCSubstring Scheme() const { return mozurl_scheme(this); }
+ nsDependentCSubstring Username() const { return mozurl_username(this); }
+ nsDependentCSubstring Password() const { return mozurl_password(this); }
+ // Will return the hostname of URL. If the hostname is an IPv6 address,
+ // it will be enclosed in square brackets, such as `[::1]`
+ nsDependentCSubstring Host() const { return mozurl_host(this); }
+ // Will return the port number, if specified, or -1
+ int32_t Port() const { return mozurl_port(this); }
+ int32_t RealPort() const { return mozurl_real_port(this); }
+ // If the URL's port number is equal to the default port, will only return the
+ // hostname, otherwise it will return a string of the form `{host}:{port}`
+ // See: https://url.spec.whatwg.org/#default-port
+ nsDependentCSubstring HostPort() const { return mozurl_host_port(this); }
+ nsDependentCSubstring FilePath() const { return mozurl_filepath(this); }
+ nsDependentCSubstring Path() const { return mozurl_path(this); }
+ nsDependentCSubstring Query() const { return mozurl_query(this); }
+ bool HasQuery() const { return mozurl_has_query(this); }
+ nsDependentCSubstring Ref() const { return mozurl_fragment(this); }
+ bool HasFragment() const { return mozurl_has_fragment(this); }
+ nsDependentCSubstring Directory() const { return mozurl_directory(this); }
+ nsDependentCSubstring PrePath() const { return mozurl_prepath(this); }
+ nsDependentCSubstring SpecNoRef() const { return mozurl_spec_no_ref(this); }
+
+ // This matches the definition of origins and base domains in nsIPrincipal for
+ // almost all URIs (some rare file:// URIs don't match and it would be hard to
+ // fix them). It definitely matches nsIPrincipal for URIs used in quota
+ // manager and there are checks in quota manager and its clients that prevent
+ // different definitions (see QuotaManager::IsPrincipalInfoValid).
+ // See also TestMozURL.cpp which enumerates a huge pile of URIs and checks
+ // that origin and base domain definitions are in sync.
+ void Origin(nsACString& aOrigin) const { mozurl_origin(this, &aOrigin); }
+ nsresult BaseDomain(nsACString& aBaseDomain) const {
+ return mozurl_base_domain(this, &aBaseDomain);
+ }
+ bool CannotBeABase() { return mozurl_cannot_be_a_base(this); }
+
+ nsresult GetCommonBase(const MozURL* aOther, MozURL** aCommon) const {
+ return mozurl_common_base(this, aOther, aCommon);
+ }
+ nsresult GetRelative(const MozURL* aOther, nsACString* aRelative) const {
+ return mozurl_relative(this, aOther, aRelative);
+ }
+
+ size_t SizeOf() { return mozurl_sizeof(this); }
+
+ class Mutator {
+ public:
+ // Calling this method will result in the creation of a new MozURL that
+ // adopts the mutator's mURL.
+ // If any of the setters failed with an error code, that error code will be
+ // returned here. It will also return an error code if Finalize is called
+ // more than once on the Mutator.
+ nsresult Finalize(MozURL** aURL) {
+ nsresult rv = GetStatus();
+ if (NS_SUCCEEDED(rv)) {
+ mURL.forget(aURL);
+ } else {
+ *aURL = nullptr;
+ }
+ return rv;
+ }
+
+ // These setter methods will return a reference to `this` so that you may
+ // chain setter operations as such:
+ //
+ // RefPtr<MozURL> url2;
+ // nsresult rv = url->Mutate().SetHostname("newhost"_ns)
+ // .SetFilePath("new/file/path"_ns)
+ // .Finalize(getter_AddRefs(url2));
+ // if (NS_SUCCEEDED(rv)) { /* use url2 */ }
+ Mutator& SetScheme(const nsACString& aScheme) {
+ if (NS_SUCCEEDED(GetStatus())) {
+ mStatus = mozurl_set_scheme(mURL, &aScheme);
+ }
+ return *this;
+ }
+ Mutator& SetUsername(const nsACString& aUser) {
+ if (NS_SUCCEEDED(GetStatus())) {
+ mStatus = mozurl_set_username(mURL, &aUser);
+ }
+ return *this;
+ }
+ Mutator& SetPassword(const nsACString& aPassword) {
+ if (NS_SUCCEEDED(GetStatus())) {
+ mStatus = mozurl_set_password(mURL, &aPassword);
+ }
+ return *this;
+ }
+ Mutator& SetHostname(const nsACString& aHost) {
+ if (NS_SUCCEEDED(GetStatus())) {
+ mStatus = mozurl_set_hostname(mURL, &aHost);
+ }
+ return *this;
+ }
+ Mutator& SetHostPort(const nsACString& aHostPort) {
+ if (NS_SUCCEEDED(GetStatus())) {
+ mStatus = mozurl_set_host_port(mURL, &aHostPort);
+ }
+ return *this;
+ }
+ Mutator& SetFilePath(const nsACString& aPath) {
+ if (NS_SUCCEEDED(GetStatus())) {
+ mStatus = mozurl_set_pathname(mURL, &aPath);
+ }
+ return *this;
+ }
+ Mutator& SetQuery(const nsACString& aQuery) {
+ if (NS_SUCCEEDED(GetStatus())) {
+ mStatus = mozurl_set_query(mURL, &aQuery);
+ }
+ return *this;
+ }
+ Mutator& SetRef(const nsACString& aRef) {
+ if (NS_SUCCEEDED(GetStatus())) {
+ mStatus = mozurl_set_fragment(mURL, &aRef);
+ }
+ return *this;
+ }
+ Mutator& SetPort(int32_t aPort) {
+ if (NS_SUCCEEDED(GetStatus())) {
+ mStatus = mozurl_set_port_no(mURL, aPort);
+ }
+ return *this;
+ }
+
+ // This method returns the status code of the setter operations.
+ // If any of the setters failed, it will return the code of the first error
+ // that occured. If none of the setters failed, it will return NS_OK.
+ // This method is useful to avoid doing expensive operations when the result
+ // would not be used because an error occurred. For example:
+ //
+ // RefPtr<MozURL> url2;
+ // MozURL::Mutator mut = url->Mutate();
+ // mut.SetScheme("!@#$"); // this would fail
+ // if (NS_SUCCEDED(mut.GetStatus())) {
+ // nsAutoCString host(ExpensiveComputing());
+ // rv = mut.SetHostname(host).Finalize(getter_AddRefs(url2));
+ // }
+ // if (NS_SUCCEEDED(rv)) { /* use url2 */ }
+ nsresult GetStatus() { return mURL ? mStatus : NS_ERROR_NOT_AVAILABLE; }
+
+ static Result<Mutator, nsresult> FromSpec(
+ const nsACString& aSpec, const MozURL* aBaseURL = nullptr) {
+ Mutator m = Mutator(aSpec, aBaseURL);
+ if (m.mURL) {
+ MOZ_ASSERT(NS_SUCCEEDED(m.mStatus));
+ return m;
+ }
+
+ MOZ_ASSERT(NS_FAILED(m.mStatus));
+ return Err(m.mStatus);
+ }
+
+ private:
+ explicit Mutator(MozURL* aUrl) : mStatus(NS_OK) {
+ mozurl_clone(aUrl, getter_AddRefs(mURL));
+ }
+
+ // This is only used to create a mutator from a string without cloning it
+ // so we avoid a pointless copy in FromSpec. It is important that we
+ // check the value of mURL afterwards.
+ explicit Mutator(const nsACString& aSpec,
+ const MozURL* aBaseURL = nullptr) {
+ mStatus = mozurl_new(getter_AddRefs(mURL), &aSpec, aBaseURL);
+ }
+ RefPtr<MozURL> mURL;
+ nsresult mStatus;
+ friend class MozURL;
+ };
+
+ Mutator Mutate() { return Mutator(this); }
+
+ // AddRef and Release are non-virtual on this type, and always call into rust.
+ void AddRef() { mozurl_addref(this); }
+ void Release() { mozurl_release(this); }
+
+ private:
+ // Make it a compile time error for C++ code to ever create, destruct, or copy
+ // MozURL objects. All of these operations will be performed by rust.
+ MozURL(); /* never defined */
+ ~MozURL(); /* never defined */
+ MozURL(const MozURL&) = delete;
+ MozURL& operator=(const MozURL&) = delete;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozURL_h__
diff --git a/netwerk/base/mozurl/cbindgen.toml b/netwerk/base/mozurl/cbindgen.toml
new file mode 100644
index 0000000000..bb5fdbd08f
--- /dev/null
+++ b/netwerk/base/mozurl/cbindgen.toml
@@ -0,0 +1,61 @@
+header = """/* 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/. */
+
+/* The MozURL type is implemented in Rust code, and uses extern "C" FFI calls to
+ * operate on the internal data structure. This file contains C declarations of
+ * these files.
+
+ * WARNING: DO NOT CALL ANY OF THESE FUNCTIONS. USE |MozURL| INSTEAD! */
+ """
+autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. */
+
+namespace mozilla {
+namespace net {
+class MozURL;
+} // namespace net
+} // namespace mozilla
+
+extern "C" {
+
+// FFI-compatible string slice struct used internally by MozURL.
+// Coerces to nsDependentCSubstring.
+struct MozURLSpecSlice {
+ char* mData;
+ uint32_t mLen;
+
+ operator nsDependentCSubstring() {
+ return nsDependentCSubstring(mData, mLen);
+ }
+};
+
+nsresult mozurl_new(mozilla::net::MozURL** aResult, const nsACString* aSpec,
+ /* optional */ const mozilla::net::MozURL* aBase);
+
+void mozurl_clone(const mozilla::net::MozURL* aThis,
+ mozilla::net::MozURL** aResult);
+
+nsresult mozurl_common_base(const mozilla::net::MozURL* aUrl1,
+ const mozilla::net::MozURL* aUrl2,
+ mozilla::net::MozURL** aResult);
+}
+
+"""
+
+include_guard = "mozilla_net_MozURL_ffi_h"
+include_version = true
+braces = "SameLine"
+line_length = 100
+tab_width = 2
+language = "C++"
+namespaces = []
+includes = ["nsError.h", "nsString.h"]
+
+[export]
+exclude = ["Gecko_StrictFileOriginPolicy", "MozURL", "SpecSlice", "mozurl_new", "mozurl_clone", "mozurl_common_base"]
+item_types = ["globals", "enums", "structs", "unions", "typedefs", "opaque", "functions", "constants"]
+
+
+[export.rename]
+"SpecSlice" = "MozURLSpecSlice"
+"MozURL" = "mozilla::net::MozURL"
diff --git a/netwerk/base/mozurl/moz.build b/netwerk/base/mozurl/moz.build
new file mode 100644
index 0000000000..0a4e522b5a
--- /dev/null
+++ b/netwerk/base/mozurl/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.net += [
+ "MozURL.h",
+]
+
+SOURCES += [
+ "MozURL.cpp",
+]
+
+FINAL_LIBRARY = "xul"
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ CbindgenHeader("MozURL_ffi.h", inputs=["/netwerk/base/mozurl"])
+
+ EXPORTS.mozilla.net += [
+ "!MozURL_ffi.h",
+ ]
diff --git a/netwerk/base/mozurl/src/lib.rs b/netwerk/base/mozurl/src/lib.rs
new file mode 100644
index 0000000000..21be5f7e8b
--- /dev/null
+++ b/netwerk/base/mozurl/src/lib.rs
@@ -0,0 +1,574 @@
+/* -*- Mode: rust; rust-indent-offset: 2 -*- */
+/* 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 url;
+use url::quirks;
+use url::{ParseOptions, Position, Url};
+
+extern crate nsstring;
+use nsstring::{nsACString, nsCString};
+
+extern crate nserror;
+use nserror::*;
+
+extern crate xpcom;
+use xpcom::interfaces::mozIThirdPartyUtil;
+use xpcom::{AtomicRefcnt, RefCounted, RefPtr};
+
+extern crate uuid;
+use uuid::Uuid;
+
+use std::fmt::Write;
+use std::marker::PhantomData;
+use std::mem;
+use std::ops;
+use std::ptr;
+use std::str;
+
+extern "C" {
+ fn Gecko_StrictFileOriginPolicy() -> bool;
+}
+
+/// Helper macro. If the expression $e is Ok(t) evaluates to t, otherwise,
+/// returns NS_ERROR_MALFORMED_URI.
+macro_rules! try_or_malformed {
+ ($e:expr) => {
+ match $e {
+ Ok(v) => v,
+ Err(_) => return NS_ERROR_MALFORMED_URI,
+ }
+ };
+}
+
+fn parser<'a>() -> ParseOptions<'a> {
+ Url::options()
+}
+
+fn default_port(scheme: &str) -> Option<u16> {
+ match scheme {
+ "ftp" => Some(21),
+ "gopher" => Some(70),
+ "http" => Some(80),
+ "https" => Some(443),
+ "ws" => Some(80),
+ "wss" => Some(443),
+ "rtsp" => Some(443),
+ "android" => Some(443),
+ _ => None,
+ }
+}
+
+/// A slice into the backing string. This type is only valid as long as the
+/// MozURL which it was pulled from is valid. In C++, this type implicitly
+/// converts to a nsDependentCString, and is an implementation detail.
+///
+/// This type exists because, unlike &str, this type is safe to return over FFI.
+#[repr(C)]
+pub struct SpecSlice<'a> {
+ data: *const u8,
+ len: u32,
+ _marker: PhantomData<&'a [u8]>,
+}
+
+impl<'a> From<&'a str> for SpecSlice<'a> {
+ fn from(s: &'a str) -> SpecSlice<'a> {
+ assert!(s.len() < u32::max_value() as usize);
+ SpecSlice {
+ data: s.as_ptr(),
+ len: s.len() as u32,
+ _marker: PhantomData,
+ }
+ }
+}
+
+/// The MozURL reference-counted threadsafe URL type. This type intentionally
+/// implements no XPCOM interfaces, and all method calls are non-virtual.
+#[repr(C)]
+pub struct MozURL {
+ pub url: Url,
+ refcnt: AtomicRefcnt,
+}
+
+impl MozURL {
+ pub fn from_url(url: Url) -> RefPtr<MozURL> {
+ // Actually allocate the URL on the heap. This is the only place we actually
+ // create a MozURL, other than in clone().
+ unsafe {
+ RefPtr::from_raw(Box::into_raw(Box::new(MozURL {
+ url: url,
+ refcnt: AtomicRefcnt::new(),
+ })))
+ .unwrap()
+ }
+ }
+}
+
+impl ops::Deref for MozURL {
+ type Target = Url;
+ fn deref(&self) -> &Url {
+ &self.url
+ }
+}
+impl ops::DerefMut for MozURL {
+ fn deref_mut(&mut self) -> &mut Url {
+ &mut self.url
+ }
+}
+
+// Memory Management for MozURL
+#[no_mangle]
+pub unsafe extern "C" fn mozurl_addref(url: &MozURL) {
+ url.refcnt.inc();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn mozurl_release(url: &MozURL) {
+ let rc = url.refcnt.dec();
+ if rc == 0 {
+ mem::drop(Box::from_raw(url as *const MozURL as *mut MozURL));
+ }
+}
+
+// xpcom::RefPtr support
+unsafe impl RefCounted for MozURL {
+ unsafe fn addref(&self) {
+ mozurl_addref(self);
+ }
+ unsafe fn release(&self) {
+ mozurl_release(self);
+ }
+}
+
+// Allocate a new MozURL object with a RefCnt of 1, and store a pointer to it
+// into url.
+#[no_mangle]
+pub extern "C" fn mozurl_new(
+ result: &mut *const MozURL,
+ spec: &nsACString,
+ base: Option<&MozURL>,
+) -> nsresult {
+ *result = ptr::null_mut();
+
+ let spec = try_or_malformed!(str::from_utf8(spec));
+ let url = if let Some(base) = base {
+ try_or_malformed!(base.url.join(spec))
+ } else {
+ try_or_malformed!(parser().parse(spec))
+ };
+
+ MozURL::from_url(url).forget(result);
+ NS_OK
+}
+
+/// Allocate a new MozURL object which is a clone of the original, and store a
+/// pointer to it into newurl.
+#[no_mangle]
+pub extern "C" fn mozurl_clone(url: &MozURL, newurl: &mut *const MozURL) {
+ MozURL::from_url(url.url.clone()).forget(newurl);
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_spec(url: &MozURL) -> SpecSlice {
+ url.as_ref().into()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_scheme(url: &MozURL) -> SpecSlice {
+ url.scheme().into()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_username(url: &MozURL) -> SpecSlice {
+ if url.cannot_be_a_base() {
+ "".into()
+ } else {
+ url.username().into()
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_password(url: &MozURL) -> SpecSlice {
+ url.password().unwrap_or("").into()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_host(url: &MozURL) -> SpecSlice {
+ url.host_str().unwrap_or("").into()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_port(url: &MozURL) -> i32 {
+ // NOTE: Gecko uses -1 to represent the default port.
+ url.port().map(|p| p as i32).unwrap_or(-1)
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_real_port(url: &MozURL) -> i32 {
+ url.port()
+ .or_else(|| default_port(url.scheme()))
+ .map(|p| p as i32)
+ .unwrap_or(-1)
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_host_port(url: &MozURL) -> SpecSlice {
+ (&url[Position::BeforeHost..Position::BeforePath]).into()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_filepath(url: &MozURL) -> SpecSlice {
+ url.path().into()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_path(url: &MozURL) -> SpecSlice {
+ (&url[Position::BeforePath..]).into()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_query(url: &MozURL) -> SpecSlice {
+ url.query().unwrap_or("").into()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_fragment(url: &MozURL) -> SpecSlice {
+ url.fragment().unwrap_or("").into()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_spec_no_ref(url: &MozURL) -> SpecSlice {
+ (&url[..Position::AfterQuery]).into()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_has_fragment(url: &MozURL) -> bool {
+ url.fragment().is_some()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_has_query(url: &MozURL) -> bool {
+ url.query().is_some()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_directory(url: &MozURL) -> SpecSlice {
+ if let Some(position) = url.path().rfind('/') {
+ url.path()[..position + 1].into()
+ } else {
+ url.path().into()
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_prepath(url: &MozURL) -> SpecSlice {
+ (&url[..Position::BeforePath]).into()
+}
+
+fn get_origin(url: &MozURL) -> Option<String> {
+ match url.scheme() {
+ "blob" | "ftp" | "http" | "https" | "ws" | "wss" => {
+ Some(url.origin().ascii_serialization())
+ }
+ "indexeddb" | "moz-extension" | "resource" => {
+ let host = url.host_str().unwrap_or("");
+
+ let port = url.port().or_else(|| default_port(url.scheme()));
+
+ if port == default_port(url.scheme()) {
+ Some(format!("{}://{}", url.scheme(), host))
+ } else {
+ Some(format!("{}://{}:{}", url.scheme(), host, port.unwrap()))
+ }
+ }
+ "file" => {
+ if unsafe { Gecko_StrictFileOriginPolicy() } {
+ Some(url[..Position::AfterPath].to_owned())
+ } else {
+ Some("file://UNIVERSAL_FILE_URI_ORIGIN".to_owned())
+ }
+ }
+ "about" | "moz-safe-about" => Some(url[..Position::AfterPath].to_owned()),
+ _ => None,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_origin(url: &MozURL, origin: &mut nsACString) {
+ let origin_str = if !url.as_ref().starts_with("about:blank") {
+ get_origin(url)
+ } else {
+ None
+ };
+
+ let origin_str = origin_str.unwrap_or_else(|| {
+ // nsIPrincipal stores the uuid, so the same uuid is returned everytime.
+ // We can't do that for MozURL because it can be used across threads.
+ // Storing uuid would mutate the object which would cause races between
+ // threads.
+ format!("moz-nullprincipal:{{{}}}", Uuid::new_v4())
+ });
+
+ // NOTE: Try to re-use the allocation we got from rust-url, and transfer
+ // ownership of the buffer to C++.
+ let mut o = nsCString::from(origin_str);
+ origin.take_from(&mut o);
+}
+
+fn get_base_domain(url: &MozURL) -> Result<Option<String>, nsresult> {
+ match url.scheme() {
+ "ftp" | "http" | "https" | "moz-extension" | "resource" => {
+ let third_party_util: RefPtr<mozIThirdPartyUtil> =
+ xpcom::components::ThirdPartyUtil::service()
+ .map_err(|_| NS_ERROR_ILLEGAL_DURING_SHUTDOWN)?;
+
+ let scheme = nsCString::from(url.scheme());
+
+ let mut host_str = url.host_str().unwrap_or("");
+
+ if host_str.starts_with('[') && host_str.ends_with(']') {
+ host_str = &host_str[1..host_str.len() - 1];
+ }
+
+ let host = nsCString::from(host_str);
+
+ unsafe {
+ let mut string = nsCString::new();
+ third_party_util
+ .GetBaseDomainFromSchemeHost(&*scheme, &*host, &mut *string)
+ .to_result()?;
+
+ // We know that GetBaseDomainFromSchemeHost returns AUTF8String, so just
+ // use unwrap().
+ Ok(Some(String::from_utf8(string.to_vec()).unwrap()))
+ }
+ }
+ "ws" | "wss" => Ok(Some(url.as_ref().to_owned())),
+ "file" => {
+ if unsafe { Gecko_StrictFileOriginPolicy() } {
+ Ok(Some(url.path().to_owned()))
+ } else {
+ Ok(Some("UNIVERSAL_FILE_URI_ORIGIN".to_owned()))
+ }
+ }
+ "about" | "moz-safe-about" | "indexeddb" => Ok(Some(url.as_ref().to_owned())),
+ _ => Ok(None),
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_base_domain(url: &MozURL, base_domain: &mut nsACString) -> nsresult {
+ let base_domain_str = if !url.as_ref().starts_with("about:blank") {
+ match get_base_domain(url) {
+ Ok(domain) => domain,
+ Err(rv) => return rv,
+ }
+ } else {
+ None
+ };
+
+ let base_domain_str = base_domain_str.unwrap_or_else(|| {
+ // See the comment in mozurl_origin about why we return a new uuid for
+ // "moz-nullprincipals".
+ format!("{{{}}}", Uuid::new_v4())
+ });
+
+ let mut bd = nsCString::from(base_domain_str);
+ base_domain.take_from(&mut bd);
+
+ NS_OK
+}
+
+// Helper macro for debug asserting that we're the only reference to MozURL.
+macro_rules! debug_assert_mut {
+ ($e:expr) => {
+ debug_assert_eq!($e.refcnt.get(), 1, "Cannot mutate an aliased MozURL!");
+ };
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_cannot_be_a_base(url: &mut MozURL) -> bool {
+ debug_assert_mut!(url);
+ url.cannot_be_a_base()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_set_scheme(url: &mut MozURL, scheme: &nsACString) -> nsresult {
+ debug_assert_mut!(url);
+ let scheme = try_or_malformed!(str::from_utf8(scheme));
+ try_or_malformed!(quirks::set_protocol(url, scheme));
+ NS_OK
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_set_username(url: &mut MozURL, username: &nsACString) -> nsresult {
+ debug_assert_mut!(url);
+ let username = try_or_malformed!(str::from_utf8(username));
+ try_or_malformed!(quirks::set_username(url, username));
+ NS_OK
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_set_password(url: &mut MozURL, password: &nsACString) -> nsresult {
+ debug_assert_mut!(url);
+ let password = try_or_malformed!(str::from_utf8(password));
+ try_or_malformed!(quirks::set_password(url, password));
+ NS_OK
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_set_host_port(url: &mut MozURL, hostport: &nsACString) -> nsresult {
+ debug_assert_mut!(url);
+ let hostport = try_or_malformed!(str::from_utf8(hostport));
+ try_or_malformed!(quirks::set_host(url, hostport));
+ NS_OK
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_set_hostname(url: &mut MozURL, host: &nsACString) -> nsresult {
+ debug_assert_mut!(url);
+ let host = try_or_malformed!(str::from_utf8(host));
+ try_or_malformed!(quirks::set_hostname(url, host));
+ NS_OK
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_set_port_no(url: &mut MozURL, new_port: i32) -> nsresult {
+ debug_assert_mut!(url);
+ if url.cannot_be_a_base() {
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ let port = match new_port {
+ new if new < 0 || u16::max_value() as i32 <= new => None,
+ new if Some(new as u16) == default_port(url.scheme()) => None,
+ new => Some(new as u16),
+ };
+ try_or_malformed!(url.set_port(port));
+ NS_OK
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_set_pathname(url: &mut MozURL, path: &nsACString) -> nsresult {
+ debug_assert_mut!(url);
+ let path = try_or_malformed!(str::from_utf8(path));
+ quirks::set_pathname(url, path);
+ NS_OK
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_set_query(url: &mut MozURL, query: &nsACString) -> nsresult {
+ debug_assert_mut!(url);
+ let query = try_or_malformed!(str::from_utf8(query));
+ quirks::set_search(url, query);
+ NS_OK
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_set_fragment(url: &mut MozURL, fragment: &nsACString) -> nsresult {
+ debug_assert_mut!(url);
+ let fragment = try_or_malformed!(str::from_utf8(fragment));
+ quirks::set_hash(url, fragment);
+ NS_OK
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_sizeof(url: &MozURL) -> usize {
+ debug_assert_mut!(url);
+ mem::size_of::<MozURL>() + url.as_str().len()
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_common_base(
+ url1: &MozURL,
+ url2: &MozURL,
+ result: &mut *const MozURL,
+) -> nsresult {
+ *result = ptr::null();
+ if url1.url == url2.url {
+ RefPtr::new(url1).forget(result);
+ return NS_OK;
+ }
+
+ if url1.scheme() != url2.scheme()
+ || url1.host() != url2.host()
+ || url1.username() != url2.username()
+ || url1.password() != url2.password()
+ || url1.port() != url2.port()
+ {
+ return NS_OK;
+ }
+
+ match (url1.path_segments(), url2.path_segments()) {
+ (Some(path1), Some(path2)) => {
+ // Take the shared prefix of path segments
+ let mut url = url1.url.clone();
+ if let Ok(mut segs) = url.path_segments_mut() {
+ segs.clear();
+ segs.extend(path1.zip(path2).take_while(|&(a, b)| a == b).map(|p| p.0));
+ } else {
+ return NS_OK;
+ }
+
+ MozURL::from_url(url).forget(result);
+ NS_OK
+ }
+ _ => NS_OK,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn mozurl_relative(
+ url1: &MozURL,
+ url2: &MozURL,
+ result: &mut nsACString,
+) -> nsresult {
+ if url1.url == url2.url {
+ result.truncate();
+ return NS_OK;
+ }
+
+ if url1.scheme() != url2.scheme()
+ || url1.host() != url2.host()
+ || url1.username() != url2.username()
+ || url1.password() != url2.password()
+ || url1.port() != url2.port()
+ {
+ result.assign(url2.as_ref());
+ return NS_OK;
+ }
+
+ match (url1.path_segments(), url2.path_segments()) {
+ (Some(mut path1), Some(mut path2)) => {
+ // Exhaust the part of the iterators that match
+ while let (Some(ref p1), Some(ref p2)) = (path1.next(), path2.next()) {
+ if p1 != p2 {
+ break;
+ }
+ }
+
+ result.truncate();
+ for _ in path1 {
+ result.append("../");
+ }
+ for p2 in path2 {
+ result.append(p2);
+ result.append("/");
+ }
+ }
+ _ => {
+ result.assign(url2.as_ref());
+ }
+ }
+ NS_OK
+}
+
+/// This type is used by nsStandardURL
+#[no_mangle]
+pub extern "C" fn rusturl_parse_ipv6addr(input: &nsACString, addr: &mut nsACString) -> nsresult {
+ let ip6 = try_or_malformed!(str::from_utf8(input));
+ let host = try_or_malformed!(url::Host::parse(ip6));
+ let _ = write!(addr, "{}", host);
+ NS_OK
+}