Bug 1160628. Speed up the URL constructor in the common case of no base URI. r=baku
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 05 May 2015 13:38:56 -0400
changeset 273834 09660c20ac706d2b3499dbe8225d5eb5cffdd945
parent 273833 4e89ee947dd553170a72e8e7d40420f1943c42a8
child 273835 a5d0c44ba3701a7cbb31fd070371f0ee6a850239
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1160628
milestone40.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 1160628. Speed up the URL constructor in the common case of no base URI. r=baku
dom/base/URL.cpp
dom/base/URL.h
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/webidl/URL.webidl
dom/workers/URL.cpp
dom/workers/URL.h
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/dom/URLBinding.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIIOService.h"
 #include "nsEscape.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsIURL.h"
+#include "nsContentUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(URL)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(URL)
   if (tmp->mSearchParams) {
@@ -37,81 +38,80 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(URL)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(URL)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-URL::URL(nsIURI* aURI)
+URL::URL(already_AddRefed<nsIURI> aURI)
   : mURI(aURI)
 {
 }
 
 bool
 URL::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector)
 {
   return URLBinding::Wrap(aCx, this, aGivenProto, aReflector);
 }
 
 /* static */ already_AddRefed<URL>
 URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
                  URL& aBase, ErrorResult& aRv)
 {
-  nsresult rv;
-  nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
-    return nullptr;
+  return Constructor(aUrl, aBase.GetURI(), aRv);
+}
+
+/* static */ already_AddRefed<URL>
+URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
+                 const Optional<nsAString>& aBase, ErrorResult& aRv)
+{
+  if (aBase.WasPassed()) {
+    return Constructor(aUrl, aBase.Value(), aRv);
   }
 
-  nsCOMPtr<nsIURI> uri;
-  rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aUrl), nullptr, aBase.GetURI(),
-                         getter_AddRefs(uri));
-  if (NS_FAILED(rv)) {
-    nsAutoString label(aUrl);
-    aRv.ThrowTypeError(MSG_INVALID_URL, &label);
-    return nullptr;
-  }
-
-  nsRefPtr<URL> url = new URL(uri);
-  return url.forget();
+  return Constructor(aUrl, nullptr, aRv);
 }
 
 /* static */ already_AddRefed<URL>
 URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
-                  const nsAString& aBase, ErrorResult& aRv)
+                 const nsAString& aBase, ErrorResult& aRv)
 {
-  nsresult rv;
-  nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
+  return Constructor(aUrl, aBase, aRv);
+}
+
+/* static */ already_AddRefed<URL>
+URL::Constructor(const nsAString& aUrl, const nsAString& aBase,
+                 ErrorResult& aRv)
+{
+  nsCOMPtr<nsIURI> baseUri;
+  nsresult rv = NS_NewURI(getter_AddRefs(baseUri), aBase, nullptr, nullptr,
+                          nsContentUtils::GetIOService());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.ThrowTypeError(MSG_INVALID_URL, &aBase);
     return nullptr;
   }
 
-  nsCOMPtr<nsIURI> baseUri;
-  rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aBase), nullptr, nullptr,
-                         getter_AddRefs(baseUri));
-  if (NS_FAILED(rv)) {
-    nsAutoString label(aBase);
-    aRv.ThrowTypeError(MSG_INVALID_URL, &label);
+  return Constructor(aUrl, baseUri, aRv);
+}
+
+/* static */
+already_AddRefed<URL>
+URL::Constructor(const nsAString& aUrl, nsIURI* aBase, ErrorResult& aRv)
+{
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl, nullptr, aBase,
+                          nsContentUtils::GetIOService());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.ThrowTypeError(MSG_INVALID_URL, &aUrl);
     return nullptr;
   }
 
-  nsCOMPtr<nsIURI> uri;
-  rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aUrl), nullptr, baseUri,
-                         getter_AddRefs(uri));
-  if (NS_FAILED(rv)) {
-    nsAutoString label(aUrl);
-    aRv.ThrowTypeError(MSG_INVALID_URL, &label);
-    return nullptr;
-  }
-
-  nsRefPtr<URL> url = new URL(uri);
+  nsRefPtr<URL> url = new URL(uri.forget());
   return url.forget();
 }
 
 void
 URL::CreateObjectURL(const GlobalObject& aGlobal,
                      File& aBlob,
                      const objectURLOptions& aOptions,
                      nsAString& aResult,
--- a/dom/base/URL.h
+++ b/dom/base/URL.h
@@ -34,28 +34,36 @@ class URLProxy;
 class URL final : public URLSearchParamsObserver
 {
   ~URL() {}
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(URL)
 
-  explicit URL(nsIURI* aURI);
+  explicit URL(already_AddRefed<nsIURI> aURI);
 
   // WebIDL methods
   bool
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector);
 
   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 Optional<nsAString>& aBase, ErrorResult& aRv);
+  // Versions of Constructor that we can share with workers and other code.
+  static already_AddRefed<URL>
+  Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
               const nsAString& aBase, ErrorResult& aRv);
+  static already_AddRefed<URL>
+  Constructor(const nsAString& aUrl, const nsAString& aBase, ErrorResult& aRv);
+  static already_AddRefed<URL>
+  Constructor(const nsAString& aUrl, nsIURI* aBase, ErrorResult& aRv);
 
   static void CreateObjectURL(const GlobalObject& aGlobal,
                               File& aBlob,
                               const objectURLOptions& aOptions,
                               nsAString& aResult,
                               ErrorResult& aError);
   static void CreateObjectURL(const GlobalObject& aGlobal,
                               DOMMediaStream& aStream,
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -141,17 +141,17 @@ ErrorResult::ThrowErrorWithMessage(va_li
   }
   mResult = errorType;
   Message* message = new Message();
   message->mErrorNumber = errorNumber;
   uint16_t argCount = dom::GetErrorMessage(nullptr, errorNumber)->argCount;
   MOZ_ASSERT(argCount <= 10);
   argCount = std::min<uint16_t>(argCount, 10);
   while (argCount--) {
-    message->mArgs.AppendElement(*va_arg(ap, nsString*));
+    message->mArgs.AppendElement(*va_arg(ap, const nsAString*));
   }
   mMessage = message;
 #ifdef DEBUG
   mHasMessage = true;
 #endif
 }
 
 void
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1044,17 +1044,17 @@ WrapNewBindingNonWrapperCachedObject(JSC
     if (!value->WrapObject(cx, JS::NullPtr(), &obj)) {
       return false;
     }
   }
 
   // We can end up here in all sorts of compartments, per above.  Make
   // sure to JS_WrapValue!
   rval.set(JS::ObjectValue(*obj));
-  return JS_WrapValue(cx, rval);
+  return MaybeWrapObjectValue(cx, rval);
 }
 
 // Create a JSObject wrapping "value", for cases when "value" is a
 // non-wrapper-cached owned object using WebIDL bindings.  "value" must implement a
 // WrapObject() method taking a JSContext, a scope, and a boolean outparam that
 // is true if the JSObject took ownership
 template <class T>
 inline bool
@@ -1092,17 +1092,17 @@ WrapNewBindingNonWrapperCachedObject(JSC
     }
 
     value.forget();
   }
 
   // We can end up here in all sorts of compartments, per above.  Make
   // sure to JS_WrapValue!
   rval.set(JS::ObjectValue(*obj));
-  return JS_WrapValue(cx, rval);
+  return MaybeWrapObjectValue(cx, rval);
 }
 
 // Helper for smart pointers (nsRefPtr/nsCOMPtr).
 template <template <typename> class SmartPtr, typename T,
           typename U=typename EnableIf<IsRefcounted<T>::value, T>::Type>
 inline bool
 WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope,
                                      const SmartPtr<T>& value,
--- a/dom/webidl/URL.webidl
+++ b/dom/webidl/URL.webidl
@@ -9,17 +9,17 @@
  * 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"),
+ Constructor(DOMString url, optional DOMString base),
  Exposed=(Window,Worker)]
 interface URL {
 };
 URL implements URLUtils;
 URL implements URLUtilsSearchParams;
 
 partial interface URL {
   [Throws]
--- a/dom/workers/URL.cpp
+++ b/dom/workers/URL.cpp
@@ -29,17 +29,17 @@
 BEGIN_WORKERS_NAMESPACE
 using mozilla::dom::GlobalObject;
 
 class URLProxy final
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URLProxy)
 
-  explicit URLProxy(mozilla::dom::URL* aURL)
+  explicit URLProxy(already_AddRefed<mozilla::dom::URL> aURL)
     : mURL(aURL)
   {
     AssertIsOnMainThread();
   }
 
   mozilla::dom::URL* URL()
   {
     return mURL;
@@ -223,79 +223,69 @@ public:
 };
 
 // This class creates a URL object on the main thread.
 class ConstructorRunnable : public WorkerMainThreadRunnable
 {
 private:
   const nsString mURL;
 
-  const nsString mBase;
+  nsString mBase; // IsVoid() if we have no base URI string.
   nsRefPtr<URLProxy> mBaseProxy;
   mozilla::ErrorResult& mRv;
 
   nsRefPtr<URLProxy> mRetval;
 
 public:
   ConstructorRunnable(WorkerPrivate* aWorkerPrivate,
-                      const nsAString& aURL, const nsAString& aBase,
+                      const nsAString& aURL, const Optional<nsAString>& aBase,
                       mozilla::ErrorResult& aRv)
   : WorkerMainThreadRunnable(aWorkerPrivate)
   , mURL(aURL)
-  , mBase(aBase)
   , mRv(aRv)
   {
+    if (aBase.WasPassed()) {
+      mBase = aBase.Value();
+    } else {
+      mBase.SetIsVoid(true);
+    }
     mWorkerPrivate->AssertIsOnWorkerThread();
   }
 
   ConstructorRunnable(WorkerPrivate* aWorkerPrivate,
                       const nsAString& aURL, URLProxy* aBaseProxy,
                       mozilla::ErrorResult& aRv)
   : WorkerMainThreadRunnable(aWorkerPrivate)
   , mURL(aURL)
   , mBaseProxy(aBaseProxy)
   , mRv(aRv)
   {
+    mBase.SetIsVoid(true);
     mWorkerPrivate->AssertIsOnWorkerThread();
   }
 
   bool
   MainThreadRun()
   {
     AssertIsOnMainThread();
 
-    nsresult rv;
-    nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
-    if (NS_FAILED(rv)) {
-      mRv.Throw(rv);
+    nsRefPtr<mozilla::dom::URL> url;
+    if (mBaseProxy) {
+      url = mozilla::dom::URL::Constructor(mURL, mBaseProxy->URI(), mRv);
+    } else if (!mBase.IsVoid()) {
+      url = mozilla::dom::URL::Constructor(mURL, mBase, mRv);
+    } else {
+      url = mozilla::dom::URL::Constructor(mURL, nullptr, mRv);
+    }
+
+    if (mRv.Failed()) {
       return true;
     }
 
-    nsCOMPtr<nsIURI> baseURL;
-
-    if (!mBaseProxy) {
-      rv = ioService->NewURI(NS_ConvertUTF16toUTF8(mBase), nullptr, nullptr,
-                             getter_AddRefs(baseURL));
-      if (NS_FAILED(rv)) {
-        mRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-        return true;
-      }
-    } else {
-      baseURL = mBaseProxy->URI();
-    }
-
-    nsCOMPtr<nsIURI> url;
-    rv = ioService->NewURI(NS_ConvertUTF16toUTF8(mURL), nullptr, baseURL,
-                           getter_AddRefs(url));
-    if (NS_FAILED(rv)) {
-      mRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-      return true;
-    }
-
-    mRetval = new URLProxy(new mozilla::dom::URL(url));
+    mRetval = new URLProxy(url.forget());
     return true;
   }
 
   URLProxy*
   GetURLProxy()
   {
     return mRetval;
   }
@@ -517,52 +507,69 @@ URL::Constructor(const GlobalObject& aGl
                  URL& aBase, ErrorResult& aRv)
 {
   JSContext* cx = aGlobal.Context();
   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
 
   nsRefPtr<ConstructorRunnable> runnable =
     new ConstructorRunnable(workerPrivate, aUrl, aBase.GetURLProxy(), aRv);
 
-  if (!runnable->Dispatch(cx)) {
-    JS_ReportPendingException(cx);
-  }
+  return FinishConstructor(cx, workerPrivate, runnable, aRv);
+}
 
-  nsRefPtr<URLProxy> proxy = runnable->GetURLProxy();
-  if (!proxy) {
-    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-    return nullptr;
-  }
+// static
+already_AddRefed<URL>
+URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
+                 const Optional<nsAString>& aBase, ErrorResult& aRv)
+{
+  JSContext* cx = aGlobal.Context();
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
 
-  nsRefPtr<URL> url = new URL(workerPrivate, proxy);
-  return url.forget();
+  nsRefPtr<ConstructorRunnable> runnable =
+    new ConstructorRunnable(workerPrivate, aUrl, aBase, aRv);
+
+  return FinishConstructor(cx, workerPrivate, runnable, aRv);
 }
 
 // static
 already_AddRefed<URL>
 URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
                  const nsAString& aBase, ErrorResult& aRv)
 {
   JSContext* cx = aGlobal.Context();
   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
 
+  Optional<nsAString> base;
+  base = &aBase;
   nsRefPtr<ConstructorRunnable> runnable =
-    new ConstructorRunnable(workerPrivate, aUrl, aBase, aRv);
+    new ConstructorRunnable(workerPrivate, aUrl, base, aRv);
+
+  return FinishConstructor(cx, workerPrivate, runnable, aRv);
+}
 
-  if (!runnable->Dispatch(cx)) {
-    JS_ReportPendingException(cx);
+// static
+already_AddRefed<URL>
+URL::FinishConstructor(JSContext* aCx, WorkerPrivate* aPrivate,
+                       ConstructorRunnable* aRunnable, ErrorResult& aRv)
+{
+  if (!aRunnable->Dispatch(aCx)) {
+    JS_ReportPendingException(aCx);
   }
 
-  nsRefPtr<URLProxy> proxy = runnable->GetURLProxy();
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  nsRefPtr<URLProxy> proxy = aRunnable->GetURLProxy();
   if (!proxy) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return nullptr;
   }
 
-  nsRefPtr<URL> url = new URL(workerPrivate, proxy);
+  nsRefPtr<URL> url = new URL(aPrivate, proxy);
   return url.forget();
 }
 
 URL::URL(WorkerPrivate* aWorkerPrivate, URLProxy* aURLProxy)
   : mWorkerPrivate(aWorkerPrivate)
   , mURLProxy(aURLProxy)
 {
   MOZ_COUNT_CTOR(workers::URL);
--- a/dom/workers/URL.h
+++ b/dom/workers/URL.h
@@ -18,16 +18,17 @@ namespace dom {
 class File;
 struct objectURLOptions;
 }
 }
 
 BEGIN_WORKERS_NAMESPACE
 
 class URLProxy;
+class ConstructorRunnable;
 
 class URL final : public mozilla::dom::URLSearchParamsObserver
 {
   typedef mozilla::dom::URLSearchParams URLSearchParams;
 
   ~URL();
 
 public:
@@ -48,16 +49,19 @@ public:
 
   // Methods for WebIDL
 
   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 Optional<nsAString>& 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,
                   File& aArg, const objectURLOptions& aOptions,
                   nsAString& aResult, ErrorResult& aRv);
 
   static void
@@ -118,16 +122,20 @@ public:
   void URLSearchParamsUpdated(URLSearchParams* aSearchParams) override;
 
 private:
   URLProxy* GetURLProxy() const
   {
     return mURLProxy;
   }
 
+  static already_AddRefed<URL>
+  FinishConstructor(JSContext* aCx, WorkerPrivate* aPrivate,
+                    ConstructorRunnable* aRunnable, ErrorResult& aRv);
+
   void CreateSearchParamsIfNeeded();
 
   void SetSearchInternal(const nsAString& aSearch);
 
   void UpdateURLSearchParams();
 
   WorkerPrivate* mWorkerPrivate;
   nsRefPtr<URLProxy> mURLProxy;