Bug 887364 - URL API for main thread. r=ehsan
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 04 Sep 2013 13:07:21 -0400
changeset 159297 f2199d73aef6c7d68fe54ba360a676d2b2108b3f
parent 159296 907989350527f6f73e5b107cce519453afd34006
child 159298 c5fda7e3b3f42c3fc8ea4fe12f9dc0c0a79d4f10
push id407
push userlsblakk@mozilla.com
push dateTue, 03 Dec 2013 03:32:50 +0000
treeherdermozilla-release@babf8c9ebc52 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs887364
milestone26.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 887364 - URL API for main thread. r=ehsan
content/base/public/nsContentUtils.h
content/base/src/Link.cpp
content/base/src/nsContentUtils.cpp
dom/base/URL.cpp
dom/base/URL.h
dom/base/test/Makefile.in
dom/base/test/test_url.html
dom/bindings/Bindings.conf
dom/bindings/test/Makefile.in
dom/bindings/test/test_bug907548.html
dom/webidl/URL.webidl
dom/workers/URL.cpp
dom/workers/URL.h
netwerk/base/src/nsStandardURL.cpp
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1585,16 +1585,17 @@ public:
    * @note this should be used for HTML5 origin determination.
    */
   static nsresult GetASCIIOrigin(nsIPrincipal* aPrincipal,
                                  nsCString& aOrigin);
   static nsresult GetASCIIOrigin(nsIURI* aURI, nsCString& aOrigin);
   static nsresult GetUTFOrigin(nsIPrincipal* aPrincipal,
                                nsString& aOrigin);
   static nsresult GetUTFOrigin(nsIURI* aURI, nsString& aOrigin);
+  static void GetUTFNonNullOrigin(nsIURI* aURI, nsString& aOrigin);
 
   /**
    * This method creates and dispatches "command" event, which implements
    * nsIDOMXULCommandEvent.
    * If aShell is not null, dispatching goes via
    * nsIPresShell::HandleDOMEventWithTarget.
    */
   static nsresult DispatchXULCommand(nsIContent* aTarget,
--- a/content/base/src/Link.cpp
+++ b/content/base/src/Link.cpp
@@ -176,42 +176,17 @@ void
 Link::SetHost(const nsAString &aHost)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
-  // We cannot simply call nsIURI::SetHost because that would treat the name as
-  // an IPv6 address (like http:://[server:443]/).  We also cannot call
-  // nsIURI::SetHostPort because that isn't implemented.  Sadfaces.
-
-  // First set the hostname.
-  nsAString::const_iterator start, end;
-  aHost.BeginReading(start);
-  aHost.EndReading(end);
-  nsAString::const_iterator iter(start);
-  (void)FindCharInReadable(':', iter, end);
-  NS_ConvertUTF16toUTF8 host(Substring(start, iter));
-  (void)uri->SetHost(host);
-
-  // Also set the port if needed.
-  if (iter != end) {
-    iter++;
-    if (iter != end) {
-      nsAutoString portStr(Substring(iter, end));
-      nsresult rv;
-      int32_t port = portStr.ToInteger(&rv);
-      if (NS_SUCCEEDED(rv)) {
-        (void)uri->SetPort(port);
-      }
-    }
-  };
-
+  (void)uri->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
   SetHrefAttribute(uri);
   return;
 }
 
 void
 Link::SetHostname(const nsAString &aHostname)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
@@ -291,24 +266,18 @@ Link::GetOrigin(nsAString &aOrigin)
   aOrigin.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     return;
   }
 
   nsString origin;
-  nsresult rv = nsContentUtils::GetUTFOrigin(uri, origin);
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  if (!aOrigin.EqualsLiteral("null")) {
-    aOrigin.Assign(origin);
-  }
+  nsContentUtils::GetUTFNonNullOrigin(uri, origin);
+  aOrigin.Assign(origin);
 }
 
 void
 Link::GetProtocol(nsAString &_protocol)
 {
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     _protocol.AssignLiteral("http");
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -5512,16 +5512,29 @@ nsContentUtils::GetUTFOrigin(nsIURI* aUR
   else {
     aOrigin.AssignLiteral("null");
   }
   
   return NS_OK;
 }
 
 /* static */
+void
+nsContentUtils::GetUTFNonNullOrigin(nsIURI* aURI, nsString& aOrigin)
+{
+  aOrigin.Truncate();
+
+  nsString origin;
+  nsresult rv = GetUTFOrigin(aURI, origin);
+  if (NS_SUCCEEDED(rv) && !origin.EqualsLiteral("null")) {
+    aOrigin.Assign(origin);
+  }
+}
+
+/* static */
 already_AddRefed<nsIDocument>
 nsContentUtils::GetDocumentFromScriptContext(nsIScriptContext *aScriptContext)
 {
   if (!aScriptContext)
     return nullptr;
 
   nsCOMPtr<nsIDOMWindow> window =
     do_QueryInterface(aScriptContext->GetGlobalObject());
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -4,23 +4,110 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "URL.h"
 
 #include "nsGlobalWindow.h"
 #include "nsIDOMFile.h"
 #include "DOMMediaStream.h"
 #include "mozilla/dom/MediaSource.h"
-#include "nsIDocument.h"
-#include "nsIPrincipal.h"
+#include "mozilla/dom/URLBinding.h"
 #include "nsHostObjectProtocolHandler.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIIOService.h"
+#include "nsEscape.h"
+#include "nsNetCID.h"
+#include "nsIURL.h"
 
 namespace mozilla {
 namespace dom {
 
+NS_IMPL_CYCLE_COLLECTION_1(URL, mWindow)
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(URL, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(URL, Release)
+
+URL::URL(nsPIDOMWindow* aWindow, nsIURI* aURI)
+  : mWindow(aWindow)
+  , mURI(aURI)
+{
+}
+
+JSObject*
+URL::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return URLBinding::Wrap(aCx, aScope, this);
+}
+
+/* static */ already_AddRefed<URL>
+URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
+                 URL& aBase, ErrorResult& aRv)
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
+  if (!window) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aUrl), nullptr, aBase.GetURI(),
+                         getter_AddRefs(uri));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return nullptr;
+  }
+
+  nsRefPtr<URL> url = new URL(window, uri);
+  return url.forget();
+}
+
+/* static */ already_AddRefed<URL>
+URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
+                  const nsAString& aBase, ErrorResult& aRv)
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
+  if (!window) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIURI> baseUri;
+  rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aBase), nullptr, nullptr,
+                         getter_AddRefs(baseUri));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aUrl), nullptr, baseUri,
+                         getter_AddRefs(uri));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return nullptr;
+  }
+
+  nsRefPtr<URL> url = new URL(window, uri);
+  return url.forget();
+}
+
 void
 URL::CreateObjectURL(const GlobalObject& aGlobal,
                      nsIDOMBlob* aBlob,
                      const objectURLOptions& aOptions,
                      nsString& aResult,
                      ErrorResult& aError)
 {
   CreateObjectURLInternal(aGlobal.GetAsSupports(), aBlob,
@@ -48,19 +135,18 @@ URL::CreateObjectURL(const GlobalObject&
   CreateObjectURLInternal(aGlobal.GetAsSupports(), &aSource,
                           NS_LITERAL_CSTRING(MEDIASOURCEURI_SCHEME), aOptions,
                           aResult, aError);
 }
 
 void
 URL::CreateObjectURLInternal(nsISupports* aGlobal, nsISupports* aObject,
                              const nsACString& aScheme,
-                             const mozilla::dom::objectURLOptions& aOptions,
-                             nsString& aResult,
-                             ErrorResult& aError)
+                             const objectURLOptions& aOptions,
+                             nsString& aResult, ErrorResult& aError)
 {
   nsCOMPtr<nsPIDOMWindow> w = do_QueryInterface(aGlobal);
   nsGlobalWindow* window = static_cast<nsGlobalWindow*>(w.get());
   NS_PRECONDITION(!window || window->IsInnerWindow(),
                   "Should be inner window");
 
   if (!window || !window->GetExtantDoc()) {
     aError.Throw(NS_ERROR_INVALID_POINTER);
@@ -106,10 +192,223 @@ URL::RevokeObjectURL(const GlobalObject&
       subsumes) {
     if (window->GetExtantDoc()) {
       window->GetExtantDoc()->UnregisterHostObjectUri(asciiurl);
     }
     nsHostObjectProtocolHandler::RemoveDataEntry(asciiurl);
   }
 }
 
+void
+URL::GetHref(nsString& aHref) const
+{
+  aHref.Truncate();
+
+  nsAutoCString href;
+  nsresult rv = mURI->GetSpec(href);
+  if (NS_SUCCEEDED(rv)) {
+    CopyUTF8toUTF16(href, aHref);
+  }
+}
+
+void
+URL::SetHref(const nsAString& aHref, ErrorResult& aRv)
+{
+  aRv = mURI->SetSpec(NS_ConvertUTF16toUTF8(aHref));
+}
+
+void
+URL::GetOrigin(nsString& aOrigin) const
+{
+  nsContentUtils::GetUTFNonNullOrigin(mURI, aOrigin);
+}
+
+void
+URL::GetProtocol(nsString& aProtocol) const
+{
+  nsCString protocol;
+  if (NS_SUCCEEDED(mURI->GetScheme(protocol))) {
+    aProtocol.Truncate();
+  }
+
+  CopyASCIItoUTF16(protocol, aProtocol);
+  aProtocol.Append(PRUnichar(':'));
+}
+
+void
+URL::SetProtocol(const nsAString& aProtocol)
+{
+  nsAString::const_iterator start, end;
+  aProtocol.BeginReading(start);
+  aProtocol.EndReading(end);
+  nsAString::const_iterator iter(start);
+
+  FindCharInReadable(':', iter, end);
+  mURI->SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)));
+}
+
+#define URL_GETTER( value, func ) \
+  value.Truncate();               \
+  nsAutoCString tmp;              \
+  nsresult rv = mURI->func(tmp);  \
+  if (NS_SUCCEEDED(rv)) {         \
+    CopyUTF8toUTF16(tmp, value);  \
+  }
+
+void
+URL::GetUsername(nsString& aUsername) const
+{
+  URL_GETTER(aUsername, GetUsername);
+}
+
+void
+URL::SetUsername(const nsAString& aUsername)
+{
+  mURI->SetUsername(NS_ConvertUTF16toUTF8(aUsername));
+}
+
+void
+URL::GetPassword(nsString& aPassword) const
+{
+  URL_GETTER(aPassword, GetPassword);
+}
+
+void
+URL::SetPassword(const nsAString& aPassword)
+{
+  mURI->SetPassword(NS_ConvertUTF16toUTF8(aPassword));
+}
+
+void
+URL::GetHost(nsString& aHost) const
+{
+  URL_GETTER(aHost, GetHostPort);
+}
+
+void
+URL::SetHost(const nsAString& aHost)
+{
+  mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
+}
+
+void
+URL::GetHostname(nsString& aHostname) const
+{
+  URL_GETTER(aHostname, GetHost);
+}
+
+void
+URL::SetHostname(const nsAString& aHostname)
+{
+  mURI->SetHost(NS_ConvertUTF16toUTF8(aHostname));
+}
+
+void
+URL::GetPort(nsString& aPort) const
+{
+  aPort.Truncate();
+
+  int32_t port;
+  nsresult rv = mURI->GetPort(&port);
+  if (NS_SUCCEEDED(rv) && port != -1) {
+    nsAutoString portStr;
+    portStr.AppendInt(port, 10);
+    aPort.Assign(portStr);
+  }
+}
+
+void
+URL::SetPort(const nsAString& aPort)
+{
+  nsresult rv;
+  nsAutoString portStr(aPort);
+  int32_t port = portStr.ToInteger(&rv);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  mURI->SetPort(port);
+}
+
+void
+URL::GetPathname(nsString& aPathname) const
+{
+  aPathname.Truncate();
+
+  nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
+  if (!url) {
+    // Do not throw!  Not having a valid URI or URL should result in an empty
+    // string.
+    return;
+  }
+
+  nsAutoCString file;
+  nsresult rv = url->GetFilePath(file);
+  if (NS_SUCCEEDED(rv)) {
+    CopyUTF8toUTF16(file, aPathname);
+  }
+}
+
+void
+URL::SetPathname(const nsAString& aPathname)
+{
+  nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
+  if (!url) {
+    // Ignore failures to be compatible with NS4.
+    return;
+  }
+
+  url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
+}
+
+void
+URL::GetSearch(nsString& aSearch) const
+{
+  aSearch.Truncate();
+
+  nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
+  if (!url) {
+    // Do not throw!  Not having a valid URI or URL should result in an empty
+    // string.
+    return;
+  }
+
+  nsAutoCString search;
+  nsresult rv = url->GetQuery(search);
+  if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
+    CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
+  }
+}
+
+void
+URL::SetSearch(const nsAString& aSearch)
+{
+  nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
+  if (!url) {
+    // Ignore failures to be compatible with NS4.
+    return;
+  }
+
+  url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
+}
+
+void
+URL::GetHash(nsString& aHash) const
+{
+  aHash.Truncate();
+
+  nsAutoCString ref;
+  nsresult rv = mURI->GetRef(ref);
+  if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
+    NS_UnescapeURL(ref); // XXX may result in random non-ASCII bytes!
+    aHash.Assign(PRUnichar('#'));
+    AppendUTF8toUTF16(ref, aHash);
+  }
+}
+
+void
+URL::SetHash(const nsAString& aHash)
+{
+  mURI->SetRef(NS_ConvertUTF16toUTF8(aHash));
+}
+
 }
 }
--- a/dom/base/URL.h
+++ b/dom/base/URL.h
@@ -1,58 +1,132 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-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/. */
 #ifndef URL_h___
 #define URL_h___
 
-#include "nscore.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsAutoPtr.h"
 #include "nsString.h"
 
 class nsIDOMBlob;
 class nsISupports;
+class nsIURI;
+class nsPIDOMWindow;
 
 namespace mozilla {
 
 class ErrorResult;
 class DOMMediaStream;
 
 namespace dom {
 
 class MediaSource;
 class GlobalObject;
 struct objectURLOptions;
 
 class URL MOZ_FINAL
 {
 public:
+  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(URL)
+  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(URL)
+
+  URL(nsPIDOMWindow* aWindow, nsIURI* aURI);
+
   // WebIDL methods
+  nsPIDOMWindow* GetParentObject() const
+  {
+    return mWindow;
+  }
+
+  JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope);
+
+  static already_AddRefed<URL>
+  Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
+              URL& aBase, ErrorResult& aRv);
+  static already_AddRefed<URL>
+  Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
+              const nsAString& aBase, ErrorResult& aRv);
+
   static void CreateObjectURL(const GlobalObject& aGlobal,
                               nsIDOMBlob* aBlob,
                               const objectURLOptions& aOptions,
                               nsString& aResult,
                               ErrorResult& aError);
   static void CreateObjectURL(const GlobalObject& aGlobal,
                               DOMMediaStream& aStream,
-                              const mozilla::dom::objectURLOptions& aOptions,
+                              const objectURLOptions& aOptions,
                               nsString& aResult,
-                              mozilla::ErrorResult& aError);
+                              ErrorResult& aError);
   static void CreateObjectURL(const GlobalObject& aGlobal,
                               MediaSource& aSource,
                               const objectURLOptions& aOptions,
                               nsString& aResult,
-                              mozilla::ErrorResult& aError);
+                              ErrorResult& aError);
   static void RevokeObjectURL(const GlobalObject& aGlobal,
                               const nsAString& aURL);
 
+  void GetHref(nsString& aHref) const;
+
+  void SetHref(const nsAString& aHref, ErrorResult& aRv);
+
+  void GetOrigin(nsString& aOrigin) const;
+
+  void GetProtocol(nsString& aProtocol) const;
+
+  void SetProtocol(const nsAString& aProtocol);
+
+  void GetUsername(nsString& aUsername) const;
+
+  void SetUsername(const nsAString& aUsername);
+
+  void GetPassword(nsString& aPassword) const;
+
+  void SetPassword(const nsAString& aPassword);
+
+  void GetHost(nsString& aHost) const;
+
+  void SetHost(const nsAString& aHost);
+
+  void GetHostname(nsString& aHostname) const;
+
+  void SetHostname(const nsAString& aHostname);
+
+  void GetPort(nsString& aPort) const;
+
+  void SetPort(const nsAString& aPort);
+
+  void GetPathname(nsString& aPathname) const;
+
+  void SetPathname(const nsAString& aPathname);
+
+  void GetSearch(nsString& aRetval) const;
+
+  void SetSearch(const nsAString& aArg);
+
+  void GetHash(nsString& aRetval) const;
+
+  void SetHash(const nsAString& aArg);
+
 private:
+  nsIURI* GetURI() const
+  {
+    return mURI;
+  }
+
   static void CreateObjectURLInternal(nsISupports* aGlobal, nsISupports* aObject,
                                       const nsACString& aScheme,
-                                      const mozilla::dom::objectURLOptions& aOptions,
+                                      const objectURLOptions& aOptions,
                                       nsString& aResult,
-                                      mozilla::ErrorResult& aError);
+                                      ErrorResult& aError);
+
+  nsRefPtr<nsPIDOMWindow> mWindow;
+  nsCOMPtr<nsIURI> mURI;
 };
 
 }
 }
 
 #endif /* URL_h___ */
--- a/dom/base/test/Makefile.in
+++ b/dom/base/test/Makefile.in
@@ -37,16 +37,17 @@ MOCHITEST_FILES = \
   test_messageChannel_post.html \
   iframe_messageChannel_post.html \
   test_messageChannel_transferable.html \
   test_messageChannel_start.html \
   test_messageChannel_pingpong.html \
   iframe_messageChannel_pingpong.html \
   test_messageChannel_unshipped.html \
   test_messageChannel_pref.html \
+  test_url.html \
   $(NULL)
 
 MOCHITEST_CHROME_FILES = \
    test_bug715041.xul \
    test_bug715041_removal.xul \
    test_domrequesthelper.xul \
    $(NULL)
 
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_url.html
@@ -0,0 +1,277 @@
+
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=887364
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 887364</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887364">Mozilla Bug 887364</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <iframe name="x" id="x"></iframe>
+  <iframe name="y" id="y"></iframe>
+</div>
+<pre id="test">
+</pre>
+  <script type="application/javascript">
+
+  /** Test for Bug 887364 **/
+  ok("URL" in window, "window.URL exists");
+
+  var tests = [
+    { url: 'http://www.abc.com',
+      base: undefined,
+      error: false,
+      href: 'http://www.abc.com/',
+      origin: 'http://www.abc.com',
+      protocol: 'http:',
+      username: '',
+      password: '',
+      host: 'www.abc.com',
+      hostname: 'www.abc.com',
+      port: '',
+      pathname: '/',
+      search: '',
+      hash: ''
+    },
+    { url: 'ftp://auser:apw@www.abc.com',
+      base: undefined,
+      error: false,
+      href: 'ftp://auser:apw@www.abc.com/',
+      origin: 'ftp://www.abc.com',
+      protocol: 'ftp:',
+      username: 'auser',
+      password: 'apw',
+      host: 'www.abc.com',
+      hostname: 'www.abc.com',
+      port: '',
+      pathname: '/',
+      search: '',
+      hash: ''
+    },
+    { url: 'http://www.abc.com:90/apath/',
+      base: undefined,
+      error: false,
+      href: 'http://www.abc.com:90/apath/',
+      origin: 'http://www.abc.com:90',
+      protocol: 'http:',
+      username: '',
+      password: '',
+      host: 'www.abc.com:90',
+      hostname: 'www.abc.com',
+      port: '90',
+      pathname: '/apath/',
+      search: '',
+      hash: ''
+    },
+    { url: 'http://www.abc.com/apath/afile.txt#ahash',
+      base: undefined,
+      error: false,
+      href: 'http://www.abc.com/apath/afile.txt#ahash',
+      origin: 'http://www.abc.com',
+      protocol: 'http:',
+      username: '',
+      password: '',
+      host: 'www.abc.com',
+      hostname: 'www.abc.com',
+      port: '',
+      pathname: '/apath/afile.txt',
+      search: '',
+      hash: '#ahash'
+    },
+    { url: 'http://example.com/?test#hash',
+      base: undefined,
+      error: false,
+      href: 'http://example.com/?test#hash',
+      origin: 'http://example.com',
+      protocol: 'http:',
+      username: '',
+      password: '',
+      host: 'example.com',
+      hostname: 'example.com',
+      port: '',
+      pathname: '/',
+      search: '?test',
+      hash: '#hash'
+    },
+    { url: 'http://example.com/?test',
+      base: undefined,
+      error: false,
+      href: 'http://example.com/?test',
+      origin: 'http://example.com',
+      protocol: 'http:',
+      username: '',
+      password: '',
+      host: 'example.com',
+      hostname: 'example.com',
+      port: '',
+      pathname: '/',
+      search: '?test',
+      hash: ''
+    },
+    { url: 'http://example.com/carrot#question%3f',
+      base: undefined,
+      error: false,
+      hash: '#question?'
+    },
+    { url: 'https://example.com:4443?',
+      base: undefined,
+      error: false,
+      protocol: 'https:',
+      port: '4443',
+      pathname: '/',
+      hash: '',
+      search: ''
+    },
+    { url: 'http://www.abc.com/apath/afile.txt#ahash?asearch',
+      base: undefined,
+      error: false,
+      href: 'http://www.abc.com/apath/afile.txt#ahash?asearch',
+      protocol: 'http:',
+      pathname: '/apath/afile.txt',
+      hash: '#ahash?asearch',
+      search: ''
+    },
+    { url: 'http://www.abc.com/apath/afile.txt?asearch#ahash',
+      base: undefined,
+      error: false,
+      href: 'http://www.abc.com/apath/afile.txt?asearch#ahash',
+      protocol: 'http:',
+      pathname: '/apath/afile.txt',
+      hash: '#ahash',
+      search: '?asearch'
+    },
+    { url: 'http://abc.com/apath/afile.txt?#ahash',
+      base: undefined,
+      error: false,
+      pathname: '/apath/afile.txt',
+      hash: '#ahash',
+      search: ''
+    },
+    { url: 'http://auser:apassword@www.abc.com:90/apath/afile.txt?asearch#ahash',
+      base: undefined,
+      error: false,
+      protocol: 'http:',
+      username: 'auser',
+      password: 'apassword',
+      host: 'www.abc.com:90',
+      hostname: 'www.abc.com',
+      port: '90',
+      pathname: '/apath/afile.txt',
+      hash: '#ahash',
+      search: '?asearch',
+      origin: 'http://www.abc.com:90'
+    },
+
+    { url: '/foo#bar',
+      base: 'www.test.org',
+      error: true,
+    },
+    { url: '/foo#bar',
+      base: null,
+      error: true,
+    },
+    { url: '/foo#bar',
+      base: 42,
+      error: true,
+    },
+    { url: 'ftp://ftp.something.net',
+      base: undefined,
+      error: false,
+      protocol: 'ftp:',
+    },
+    { url: 'file:///tmp/file',
+      base: undefined,
+      error: false,
+      protocol: 'file:',
+    },
+    { url: 'gopher://gopher.something.net',
+      base: undefined,
+      error: false,
+      protocol: 'gopher:',
+    },
+    { url: 'ws://ws.something.net',
+      base: undefined,
+      error: false,
+      protocol: 'ws:',
+    },
+    { url: 'wss://ws.something.net',
+      base: undefined,
+      error: false,
+      protocol: 'wss:',
+    },
+    { url: 'foo://foo.something.net',
+      base: undefined,
+      error: false,
+      protocol: 'foo:',
+    },
+  ];
+
+  while(tests.length) {
+    var test = tests.shift();
+
+    var error = false;
+    var url;
+    try {
+      if (test.base) {
+        url = new URL(test.url, test.base);
+      } else {
+        url = new URL(test.url);
+      }
+    } catch(e) {
+      error = true;
+    }
+
+    is(test.error, error, "Error creating URL");
+    if (test.error) {
+      continue;
+    }
+
+    if ('href' in test) is(url.href, test.href, "href");
+    if ('origin' in test) is(url.origin, test.origin, "origin");
+    if ('protocol' in test) is(url.protocol, test.protocol, "protocol");
+    if ('username' in test) is(url.username, test.username, "username");
+    if ('password' in test) is(url.password, test.password, "password");
+    if ('host' in test) is(url.host, test.host, "host");
+    if ('hostname' in test) is(url.hostname, test.hostname, "hostname");
+    if ('port' in test) is(url.port, test.port, "port");
+    if ('pathname' in test) is(url.pathname, test.pathname, "pathname");
+    if ('search' in test) is(url.search, test.search, "search");
+    if ('hash' in test) is(url.hash, test.hash, "hash");
+
+    url = new URL('https://www.example.net/what#foo?bar');
+    ok(url, "Url exists!");
+
+    if ('href' in test) url.href = test.href;
+    if ('protocol' in test) url.protocol = test.protocol;
+    if ('username' in test && test.username) url.username = test.username;
+    if ('password' in test && test.password) url.password = test.password;
+    if ('host' in test) url.host = test.host;
+    if ('hostname' in test) url.hostname = test.hostname;
+    if ('port' in test) url.port = test.port;
+    if ('pathname' in test) url.pathname = test.pathname;
+    if ('search' in test) url.search = test.search;
+    if ('hash' in test) url.hash = test.hash;
+
+    if ('href' in test) is(url.href, test.href, "href");
+    if ('origin' in test) is(url.origin, test.origin, "origin");
+    if ('protocol' in test) is(url.protocol, test.protocol, "protocol");
+    if ('username' in test) is(url.username, test.username, "username");
+    if ('password' in test) is(url.password, test.password, "password");
+    if ('host' in test) is(url.host, test.host, "host");
+    if ('hostname' in test) is(test.hostname, url.hostname, "hostname");
+    if ('port' in test) is(test.port, url.port, "port");
+    if ('pathname' in test) is(test.pathname, url.pathname, "pathname");
+    if ('search' in test) is(test.search, url.search, "search");
+    if ('hash' in test) is(test.hash, url.hash, "hash");
+  }
+
+  </script>
+</body>
+</html>
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1227,22 +1227,22 @@ DOMInterfaces = {
     'nativeType': 'nsDOMUIEvent',
 },
 
 'UndoManager': {
     'implicitJSContext' : [ 'undo', 'redo', 'transact' ],
 },
 
 'URL' : [{
-    'concrete': False,
+    'wrapperCache' : False,
+    'nativeOwnership': 'refcounted',
 },
 {
     'implicitJSContext': [ 'createObjectURL', 'revokeObjectURL' ],
     'workers': True,
-    'concrete': False,
 }],
 
 'VTTCue': {
     'nativeType': 'mozilla::dom::TextTrackCue'
 },
 
 'WebGLActiveInfo': {
     'nativeType': 'mozilla::WebGLActiveInfo',
--- a/dom/bindings/test/Makefile.in
+++ b/dom/bindings/test/Makefile.in
@@ -70,17 +70,16 @@ MOCHITEST_FILES := \
   test_exceptionThrowing.html \
   test_bug852846.html \
   test_bug862092.html \
   test_bug560072.html \
   test_lenientThis.html \
   test_ByteString.html \
   test_exception_messages.html \
   test_bug707564.html \
-  test_bug907548.html \
   test_defineProperty.html \
   $(NULL)
 
 MOCHITEST_CHROME_FILES = \
   test_bug775543.html \
   test_bug707564-chrome.html \
   $(NULL)
 
deleted file mode 100644
--- a/dom/bindings/test/test_bug907548.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=907548
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 907548</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="application/javascript">
-
-  /** Test for Bug 907548 **/
-  ok(!URL.prototype,
-     "URL.prototype should be undefined unless the URL API is implemented");
-
-
-  </script>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=907548">Mozilla Bug 907548</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-</pre>
-</body>
-</html>
--- a/dom/webidl/URL.webidl
+++ b/dom/webidl/URL.webidl
@@ -1,22 +1,30 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-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/.
  *
  * The origins of this IDL file are
+ * http://url.spec.whatwg.org/#api
  * http://dev.w3.org/2006/webapi/FileAPI/#creating-revoking
  * http://dev.w3.org/2011/webrtc/editor/getusermedia.html#url
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
+// [Constructor(DOMString url, optional (URL or DOMString) base = "about:blank")]
+[Constructor(DOMString url, URL base),
+ Constructor(DOMString url, optional DOMString base = "about:blank")]
 interface URL {
+};
+URL implements URLUtils;
+
+partial interface URL {
   [Throws]
   static DOMString? createObjectURL(Blob blob, optional objectURLOptions options);
   [Throws]
   static DOMString? createObjectURL(MediaStream stream, optional objectURLOptions options);
   static void revokeObjectURL(DOMString url);
 };
 
 dictionary objectURLOptions
--- a/dom/workers/URL.cpp
+++ b/dom/workers/URL.cpp
@@ -221,16 +221,139 @@ public:
 
     if (!window) {
       mWorkerPrivate->UnregisterHostObjectURI(url);
     }
   }
 };
 
 // static
+URL*
+URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
+                 URL& aBase, ErrorResult& aRv)
+{
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  return nullptr;
+}
+
+// static
+URL*
+URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
+                 const nsAString& aBase, ErrorResult& aRv)
+{
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  return nullptr;
+}
+
+void
+URL::GetHref(nsString& aHref) const
+{
+}
+
+void
+URL::SetHref(const nsAString& aHref, ErrorResult& aRv)
+{
+}
+
+void
+URL::GetOrigin(nsString& aOrigin) const
+{
+}
+
+void
+URL::GetProtocol(nsString& aProtocol) const
+{
+}
+
+void
+URL::SetProtocol(const nsAString& aProtocol)
+{
+}
+
+void
+URL::GetUsername(nsString& aUsername) const
+{
+}
+
+void
+URL::SetUsername(const nsAString& aUsername)
+{
+}
+
+void
+URL::GetPassword(nsString& aPassword) const
+{
+}
+
+void
+URL::SetPassword(const nsAString& aPassword)
+{
+}
+
+void
+URL::GetHost(nsString& aHost) const
+{
+}
+
+void
+URL::SetHost(const nsAString& aHost)
+{
+}
+
+void
+URL::GetHostname(nsString& aHostname) const
+{
+}
+
+void
+URL::SetHostname(const nsAString& aHostname)
+{
+}
+
+void
+URL::GetPort(nsString& aPort) const
+{
+}
+
+void
+URL::SetPort(const nsAString& aPort)
+{
+}
+
+void
+URL::GetPathname(nsString& aPathname) const
+{
+}
+
+void
+URL::SetPathname(const nsAString& aPathname)
+{
+}
+
+void
+URL::GetSearch(nsString& aSearch) const
+{
+}
+
+void
+URL::SetSearch(const nsAString& aSearch)
+{
+}
+
+void
+URL::GetHash(nsString& aHash) const
+{
+}
+
+void
+URL::SetHash(const nsAString& aHash)
+{
+}
+
+// static
 void
 URL::CreateObjectURL(const GlobalObject& aGlobal, JSObject* aBlob,
                      const mozilla::dom::objectURLOptions& aOptions,
                      nsString& aResult, mozilla::ErrorResult& aRv)
 {
   JSContext* cx = aGlobal.GetContext();
   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
 
--- a/dom/workers/URL.h
+++ b/dom/workers/URL.h
@@ -11,25 +11,82 @@
 
 #include "EventTarget.h"
 
 BEGIN_WORKERS_NAMESPACE
 
 class URL : public EventTarget
 {
 public: // Methods for WebIDL
+  static URL*
+  Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
+              URL& aBase, ErrorResult& aRv);
+  static URL*
+  Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
+              const nsAString& aBase, ErrorResult& aRv);
+
   static void
   CreateObjectURL(const GlobalObject& aGlobal,
                   JSObject* aArg, const objectURLOptions& aOptions,
                   nsString& aResult, ErrorResult& aRv);
 
   static void
   CreateObjectURL(const GlobalObject& aGlobal,
                   JSObject& aArg, const objectURLOptions& aOptions,
                   nsString& aResult, ErrorResult& aRv);
 
   static void
   RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl);
+
+  void GetHref(nsString& aHref) const;
+
+  void SetHref(const nsAString& aHref, ErrorResult& aRv);
+
+  void GetOrigin(nsString& aOrigin) const;
+
+  void GetProtocol(nsString& aProtocol) const;
+
+  void SetProtocol(const nsAString& aProtocol);
+
+  void GetUsername(nsString& aUsername) const;
+
+  void SetUsername(const nsAString& aUsername);
+
+  void GetPassword(nsString& aPassword) const;
+
+  void SetPassword(const nsAString& aPassword);
+
+  void GetHost(nsString& aHost) const;
+
+  void SetHost(const nsAString& aHost);
+
+  void GetHostname(nsString& aHostname) const;
+
+  void SetHostname(const nsAString& aHostname);
+
+  void GetPort(nsString& aPort) const;
+
+  void SetPort(const nsAString& aPort);
+
+  void GetPathname(nsString& aPathname) const;
+
+  void SetPathname(const nsAString& aPathname);
+
+  void GetSearch(nsString& aSearch) const;
+
+  void SetSearch(const nsAString& aSearch);
+
+  void GetHash(nsString& aHost) const;
+
+  void SetHash(const nsAString& aHash);
+
+private:
+  mozilla::dom::URL* GetURL() const
+  {
+    return mURL;
+  }
+
+  nsRefPtr<mozilla::dom::URL> mURL;
 };
 
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_url_h__ */
--- a/netwerk/base/src/nsStandardURL.cpp
+++ b/netwerk/base/src/nsStandardURL.cpp
@@ -1404,23 +1404,49 @@ nsStandardURL::SetPassword(const nsACStr
         mPassword.mLen = escPassword.Length();
         mAuthority.mLen += shift;
         ShiftFromHost(shift);
     }
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsStandardURL::SetHostPort(const nsACString &value)
+nsStandardURL::SetHostPort(const nsACString &aValue)
 {
     ENSURE_MUTABLE();
 
-    // XXX needs implementation!!
-    NS_NOTREACHED("not implemented");
-    return NS_ERROR_NOT_IMPLEMENTED;
+  // We cannot simply call nsIURI::SetHost because that would treat the name as
+  // an IPv6 address (like http:://[server:443]/).  We also cannot call
+  // nsIURI::SetHostPort because that isn't implemented.  Sadfaces.
+
+  // First set the hostname.
+  nsACString::const_iterator start, end;
+  aValue.BeginReading(start);
+  aValue.EndReading(end);
+  nsACString::const_iterator iter(start);
+  FindCharInReadable(':', iter, end);
+
+  nsresult rv = SetHost(Substring(start, iter));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Also set the port if needed.
+  if (iter != end) {
+    iter++;
+    if (iter != end) {
+      nsCString portStr(Substring(iter, end));
+      nsresult rv;
+      int32_t port = portStr.ToInteger(&rv);
+      if (NS_SUCCEEDED(rv)) {
+        rv = SetPort(port);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+  }
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStandardURL::SetHost(const nsACString &input)
 {
     ENSURE_MUTABLE();
 
     const nsPromiseFlatCString &flat = PromiseFlatCString(input);