Bug 1169044 - Patch 2 - Split URLSearchParams parsing logic into non-CCed URLParams. r=baku
authorNikhil Marathe <nsm.nikhil@gmail.com>
Thu, 04 Jun 2015 13:45:24 -0700
changeset 282607 67b6e55eb4acb234e5d847be5b0d80110dee6862
parent 282606 a8473627ca5f30a8bba16b9bb0a2a99d9d59f3a5
child 282608 66ea7374c23d9e22d4eb9cc11f9631530f485d59
push id897
push userjlund@mozilla.com
push dateMon, 14 Sep 2015 18:56:12 +0000
treeherdermozilla-release@9411e2d2b214 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1169044
milestone41.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 1169044 - Patch 2 - Split URLSearchParams parsing logic into non-CCed URLParams. r=baku
caps/BasePrincipal.cpp
dom/base/URLSearchParams.cpp
dom/base/URLSearchParams.h
dom/fetch/Fetch.cpp
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -16,58 +16,58 @@
 #include "nsScriptSecurityManager.h"
 
 #include "mozilla/dom/CSPDictionariesBinding.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/URLSearchParams.h"
 
 namespace mozilla {
 
-using dom::URLSearchParams;
+using dom::URLParams;
 
 void
 OriginAttributes::CreateSuffix(nsACString& aStr) const
 {
   MOZ_RELEASE_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
-  nsRefPtr<URLSearchParams> usp = new URLSearchParams(nullptr);
+  UniquePtr<URLParams> params(new URLParams());
   nsAutoString value;
 
   if (mAppId != nsIScriptSecurityManager::NO_APP_ID) {
     value.AppendInt(mAppId);
-    usp->Set(NS_LITERAL_STRING("appId"), value);
+    params->Set(NS_LITERAL_STRING("appId"), value);
   }
 
   if (mInBrowser) {
-    usp->Set(NS_LITERAL_STRING("inBrowser"), NS_LITERAL_STRING("1"));
+    params->Set(NS_LITERAL_STRING("inBrowser"), NS_LITERAL_STRING("1"));
   }
 
   aStr.Truncate();
 
-  usp->Serialize(value);
+  params->Serialize(value);
   if (!value.IsEmpty()) {
     aStr.AppendLiteral("!");
     aStr.Append(NS_ConvertUTF16toUTF8(value));
   }
 }
 
 namespace {
 
 class MOZ_STACK_CLASS PopulateFromSuffixIterator final
-  : public URLSearchParams::ForEachIterator
+  : public URLParams::ForEachIterator
 {
 public:
   explicit PopulateFromSuffixIterator(OriginAttributes* aOriginAttributes)
     : mOriginAttributes(aOriginAttributes)
   {
     MOZ_ASSERT(aOriginAttributes);
   }
 
-  bool URLSearchParamsIterator(const nsString& aName,
-                               const nsString& aValue) override
+  bool URLParamsIterator(const nsString& aName,
+                         const nsString& aValue) override
   {
     if (aName.EqualsLiteral("appId")) {
       nsresult rv;
       mOriginAttributes->mAppId = aValue.ToInteger(&rv);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return false;
       }
 
@@ -103,21 +103,21 @@ OriginAttributes::PopulateFromSuffix(con
   if (aStr.IsEmpty()) {
     return true;
   }
 
   if (aStr[0] != '!') {
     return false;
   }
 
-  nsRefPtr<URLSearchParams> usp = new URLSearchParams(nullptr);
-  usp->ParseInput(Substring(aStr, 1, aStr.Length() - 1));
+  UniquePtr<URLParams> params(new URLParams());
+  params->ParseInput(Substring(aStr, 1, aStr.Length() - 1));
 
   PopulateFromSuffixIterator iterator(this);
-  return usp->ForEach(iterator);
+  return params->ForEach(iterator);
 }
 
 void
 OriginAttributes::CookieJar(nsACString& aStr)
 {
   mozilla::GetJarPrefix(mAppId, mInBrowser, aStr);
 }
 
--- a/dom/base/URLSearchParams.cpp
+++ b/dom/base/URLSearchParams.cpp
@@ -7,116 +7,144 @@
 #include "URLSearchParams.h"
 #include "mozilla/dom/URLSearchParamsBinding.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "nsDOMString.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLSearchParams, mObserver)
-NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-URLSearchParams::URLSearchParams(URLSearchParamsObserver* aObserver)
-  : mObserver(aObserver)
-{
-}
-
-URLSearchParams::~URLSearchParams()
-{
-  DeleteAll();
-}
-
-JSObject*
-URLSearchParams::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+bool
+URLParams::Has(const nsAString& aName)
 {
-  return URLSearchParamsBinding::Wrap(aCx, this, aGivenProto);
-}
+  for (uint32_t i = 0, len = mParams.Length(); i < len; ++i) {
+    if (mParams[i].mKey.Equals(aName)) {
+      return true;
+    }
+  }
 
-/* static */ already_AddRefed<URLSearchParams>
-URLSearchParams::Constructor(const GlobalObject& aGlobal,
-                             const nsAString& aInit,
-                             ErrorResult& aRv)
-{
-  nsRefPtr<URLSearchParams> sp = new URLSearchParams(nullptr);
-  sp->ParseInput(NS_ConvertUTF16toUTF8(aInit));
-  return sp.forget();
-}
-
-/* static */ already_AddRefed<URLSearchParams>
-URLSearchParams::Constructor(const GlobalObject& aGlobal,
-                             URLSearchParams& aInit,
-                             ErrorResult& aRv)
-{
-  nsRefPtr<URLSearchParams> sp = new URLSearchParams(nullptr);
-  sp->mSearchParams = aInit.mSearchParams;
-  return sp.forget();
+  return false;
 }
 
 void
-URLSearchParams::ParseInput(const nsACString& aInput)
+URLParams::Get(const nsAString& aName, nsString& aRetval)
 {
-  // Remove all the existing data before parsing a new input.
-  DeleteAll();
-
-  nsACString::const_iterator start, end;
-  aInput.BeginReading(start);
-  aInput.EndReading(end);
-  nsACString::const_iterator iter(start);
-
-  while (start != end) {
-    nsAutoCString string;
-
-    if (FindCharInReadable('&', iter, end)) {
-      string.Assign(Substring(start, iter));
-      start = ++iter;
-    } else {
-      string.Assign(Substring(start, end));
-      start = end;
-    }
-
-    if (string.IsEmpty()) {
-      continue;
-    }
+  SetDOMStringToNull(aRetval);
 
-    nsACString::const_iterator eqStart, eqEnd;
-    string.BeginReading(eqStart);
-    string.EndReading(eqEnd);
-    nsACString::const_iterator eqIter(eqStart);
-
-    nsAutoCString name;
-    nsAutoCString value;
-
-    if (FindCharInReadable('=', eqIter, eqEnd)) {
-      name.Assign(Substring(eqStart, eqIter));
+  for (uint32_t i = 0, len = mParams.Length(); i < len; ++i) {
+    if (mParams[i].mKey.Equals(aName)) {
+      aRetval.Assign(mParams[i].mValue);
+      break;
+    }
+  }
+}
 
-      ++eqIter;
-      value.Assign(Substring(eqIter, eqEnd));
-    } else {
-      name.Assign(string);
-    }
+void
+URLParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
+{
+  aRetval.Clear();
 
-    nsAutoString decodedName;
-    DecodeString(name, decodedName);
-
-    nsAutoString decodedValue;
-    DecodeString(value, decodedValue);
-
-    AppendInternal(decodedName, decodedValue);
+  for (uint32_t i = 0, len = mParams.Length(); i < len; ++i) {
+    if (mParams[i].mKey.Equals(aName)) {
+      aRetval.AppendElement(mParams[i].mValue);
+    }
   }
 }
 
 void
-URLSearchParams::DecodeString(const nsACString& aInput, nsAString& aOutput)
+URLParams::Append(const nsAString& aName, const nsAString& aValue)
+{
+  Param* param = mParams.AppendElement();
+  param->mKey = aName;
+  param->mValue = aValue;
+}
+
+void
+URLParams::Set(const nsAString& aName, const nsAString& aValue)
+{
+  Param* param = nullptr;
+  for (uint32_t i = 0, len = mParams.Length(); i < len;) {
+    if (!mParams[i].mKey.Equals(aName)) {
+      ++i;
+      continue;
+    }
+    if (!param) {
+      param = &mParams[i];
+      ++i;
+      continue;
+    }
+    // Remove duplicates.
+    mParams.RemoveElementAt(i);
+    --len;
+  }
+
+  if (!param) {
+    param = mParams.AppendElement();
+    param->mKey = aName;
+  }
+
+  param->mValue = aValue;
+}
+
+bool
+URLParams::Delete(const nsAString& aName)
+{
+  bool found = false;
+  for (uint32_t i = 0; i < mParams.Length();) {
+    if (mParams[i].mKey.Equals(aName)) {
+      mParams.RemoveElementAt(i);
+      found = true;
+    } else {
+      ++i;
+    }
+  }
+
+  return found;
+}
+
+void
+URLParams::ConvertString(const nsACString& aInput, nsAString& aOutput)
+{
+  aOutput.Truncate();
+
+  if (!mDecoder) {
+    mDecoder = EncodingUtils::DecoderForEncoding("UTF-8");
+    if (!mDecoder) {
+      MOZ_ASSERT(mDecoder, "Failed to create a decoder.");
+      return;
+    }
+  }
+
+  int32_t inputLength = aInput.Length();
+  int32_t outputLength = 0;
+
+  nsresult rv = mDecoder->GetMaxLength(aInput.BeginReading(), inputLength,
+                                       &outputLength);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  if (!aOutput.SetLength(outputLength, fallible)) {
+    return;
+  }
+
+  int32_t newOutputLength = outputLength;
+  rv = mDecoder->Convert(aInput.BeginReading(), &inputLength,
+                         aOutput.BeginWriting(), &newOutputLength);
+  if (NS_FAILED(rv)) {
+    aOutput.Truncate();
+    return;
+  }
+  if (newOutputLength < outputLength) {
+    aOutput.Truncate(newOutputLength);
+  }
+}
+
+void
+URLParams::DecodeString(const nsACString& aInput, nsAString& aOutput)
 {
   nsACString::const_iterator start, end;
   aInput.BeginReading(start);
   aInput.EndReading(end);
 
   nsCString unescaped;
 
   while (start != end) {
@@ -163,157 +191,66 @@ URLSearchParams::DecodeString(const nsAC
     unescaped.Append(*start);
     ++start;
   }
 
   ConvertString(unescaped, aOutput);
 }
 
 void
-URLSearchParams::ConvertString(const nsACString& aInput, nsAString& aOutput)
+URLParams::ParseInput(const nsACString& aInput)
 {
-  aOutput.Truncate();
-
-  if (!mDecoder) {
-    mDecoder = EncodingUtils::DecoderForEncoding("UTF-8");
-    if (!mDecoder) {
-      MOZ_ASSERT(mDecoder, "Failed to create a decoder.");
-      return;
-    }
-  }
-
-  int32_t inputLength = aInput.Length();
-  int32_t outputLength = 0;
+  // Remove all the existing data before parsing a new input.
+  DeleteAll();
 
-  nsresult rv = mDecoder->GetMaxLength(aInput.BeginReading(), inputLength,
-                                       &outputLength);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  if (!aOutput.SetLength(outputLength, fallible)) {
-    return;
-  }
-
-  int32_t newOutputLength = outputLength;
-  rv = mDecoder->Convert(aInput.BeginReading(), &inputLength,
-                         aOutput.BeginWriting(), &newOutputLength);
-  if (NS_FAILED(rv)) {
-    aOutput.Truncate();
-    return;
-  }
+  nsACString::const_iterator start, end;
+  aInput.BeginReading(start);
+  aInput.EndReading(end);
+  nsACString::const_iterator iter(start);
 
-  if (newOutputLength < outputLength) {
-    aOutput.Truncate(newOutputLength);
-  }
-}
-
-void
-URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
-{
-  SetDOMStringToNull(aRetval);
-
-  for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
-    if (mSearchParams[i].mKey.Equals(aName)) {
-      aRetval.Assign(mSearchParams[i].mValue);
-      break;
-    }
-  }
-}
+  while (start != end) {
+    nsAutoCString string;
 
-void
-URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
-{
-  aRetval.Clear();
-
-  for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
-    if (mSearchParams[i].mKey.Equals(aName)) {
-      aRetval.AppendElement(mSearchParams[i].mValue);
+    if (FindCharInReadable('&', iter, end)) {
+      string.Assign(Substring(start, iter));
+      start = ++iter;
+    } else {
+      string.Assign(Substring(start, end));
+      start = end;
     }
-  }
-}
 
-void
-URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
-{
-  Param* param = nullptr;
-  for (uint32_t i = 0, len = mSearchParams.Length(); i < len;) {
-    if (!mSearchParams[i].mKey.Equals(aName)) {
-      ++i;
+    if (string.IsEmpty()) {
       continue;
     }
-    if (!param) {
-      param = &mSearchParams[i];
-      ++i;
-      continue;
-    }
-    // Remove duplicates.
-    mSearchParams.RemoveElementAt(i);
-    --len;
-  }
 
-  if (!param) {
-    param = mSearchParams.AppendElement();
-    param->mKey = aName;
-  }
-
-  param->mValue = aValue;
+    nsACString::const_iterator eqStart, eqEnd;
+    string.BeginReading(eqStart);
+    string.EndReading(eqEnd);
+    nsACString::const_iterator eqIter(eqStart);
 
-  NotifyObserver();
-}
+    nsAutoCString name;
+    nsAutoCString value;
 
-void
-URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
-{
-  AppendInternal(aName, aValue);
-  NotifyObserver();
-}
-
-void
-URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue)
-{
-  Param* param = mSearchParams.AppendElement();
-  param->mKey = aName;
-  param->mValue = aValue;
-}
+    if (FindCharInReadable('=', eqIter, eqEnd)) {
+      name.Assign(Substring(eqStart, eqIter));
 
-bool
-URLSearchParams::Has(const nsAString& aName)
-{
-  for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
-    if (mSearchParams[i].mKey.Equals(aName)) {
-      return true;
+      ++eqIter;
+      value.Assign(Substring(eqIter, eqEnd));
+    } else {
+      name.Assign(string);
     }
-  }
-
-  return false;
-}
 
-void
-URLSearchParams::Delete(const nsAString& aName)
-{
-  bool found = false;
-  for (uint32_t i = 0; i < mSearchParams.Length();) {
-    if (mSearchParams[i].mKey.Equals(aName)) {
-      mSearchParams.RemoveElementAt(i);
-      found = true;
-    } else {
-      ++i;
-    }
+    nsAutoString decodedName;
+    DecodeString(name, decodedName);
+
+    nsAutoString decodedValue;
+    DecodeString(value, decodedValue);
+
+    Append(decodedName, decodedValue);
   }
-
-  if (found) {
-    NotifyObserver();
-  }
-}
-
-void
-URLSearchParams::DeleteAll()
-{
-  mSearchParams.Clear();
 }
 
 namespace {
 
 void SerializeString(const nsCString& aInput, nsAString& aValue)
 {
   const unsigned char* p = (const unsigned char*) aInput.get();
 
@@ -333,34 +270,141 @@ void SerializeString(const nsCString& aI
 
     ++p;
   }
 }
 
 } // anonymous namespace
 
 void
-URLSearchParams::Serialize(nsAString& aValue) const
+URLParams::Serialize(nsAString& aValue) const
 {
   aValue.Truncate();
   bool first = true;
 
-  for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
+  for (uint32_t i = 0, len = mParams.Length(); i < len; ++i) {
     if (first) {
       first = false;
     } else {
       aValue.Append('&');
     }
 
-    SerializeString(NS_ConvertUTF16toUTF8(mSearchParams[i].mKey), aValue);
+    SerializeString(NS_ConvertUTF16toUTF8(mParams[i].mKey), aValue);
     aValue.Append('=');
-    SerializeString(NS_ConvertUTF16toUTF8(mSearchParams[i].mValue), aValue);
+    SerializeString(NS_ConvertUTF16toUTF8(mParams[i].mValue), aValue);
   }
 }
 
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLSearchParams, mObserver)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+URLSearchParams::URLSearchParams(URLSearchParamsObserver* aObserver)
+  : mParams(new URLParams())
+{
+}
+
+URLSearchParams::URLSearchParams(const URLSearchParams& aOther)
+  : mParams(new URLParams(*aOther.mParams.get()))
+{
+}
+
+URLSearchParams::~URLSearchParams()
+{
+  DeleteAll();
+}
+
+JSObject*
+URLSearchParams::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return URLSearchParamsBinding::Wrap(aCx, this, aGivenProto);
+}
+
+/* static */ already_AddRefed<URLSearchParams>
+URLSearchParams::Constructor(const GlobalObject& aGlobal,
+                             const nsAString& aInit,
+                             ErrorResult& aRv)
+{
+  nsRefPtr<URLSearchParams> sp = new URLSearchParams(nullptr);
+  sp->ParseInput(NS_ConvertUTF16toUTF8(aInit));
+  return sp.forget();
+}
+
+/* static */ already_AddRefed<URLSearchParams>
+URLSearchParams::Constructor(const GlobalObject& aGlobal,
+                             URLSearchParams& aInit,
+                             ErrorResult& aRv)
+{
+  nsRefPtr<URLSearchParams> sp = new URLSearchParams(aInit);
+  return sp.forget();
+}
+
+void
+URLSearchParams::ParseInput(const nsACString& aInput)
+{
+  mParams->ParseInput(aInput);
+}
+
+void
+URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
+{
+  return mParams->Get(aName, aRetval);
+}
+
+void
+URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
+{
+  return mParams->GetAll(aName, aRetval);
+}
+
+void
+URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
+{
+  mParams->Set(aName, aValue);
+  NotifyObserver();
+}
+
+void
+URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
+{
+  mParams->Append(aName, aValue);
+  NotifyObserver();
+}
+
+bool
+URLSearchParams::Has(const nsAString& aName)
+{
+  return mParams->Has(aName);
+}
+
+void
+URLSearchParams::Delete(const nsAString& aName)
+{
+  if (mParams->Delete(aName)) {
+    NotifyObserver();
+  }
+}
+
+void
+URLSearchParams::DeleteAll()
+{
+  mParams->DeleteAll();
+}
+
+void
+URLSearchParams::Serialize(nsAString& aValue) const
+{
+  mParams->Serialize(aValue);
+}
+
 void
 URLSearchParams::NotifyObserver()
 {
   if (mObserver) {
     mObserver->URLSearchParamsUpdated(this);
   }
 }
 
--- a/dom/base/URLSearchParams.h
+++ b/dom/base/URLSearchParams.h
@@ -26,27 +26,103 @@ public:
 
   virtual void URLSearchParamsUpdated(URLSearchParams* aFromThis) = 0;
 };
 
 // This class is used in BasePrincipal and it's _extremely_ important that the
 // attributes are kept in the correct order. If this changes, please, update
 // BasePrincipal code.
 
+class URLParams final
+{
+public:
+  URLParams() {}
+
+  ~URLParams()
+  {
+    DeleteAll();
+  }
+
+  explicit URLParams(const URLParams& aOther)
+    : mParams(aOther.mParams)
+  {}
+
+  explicit URLParams(const URLParams&& aOther)
+    : mParams(Move(aOther.mParams))
+  {}
+
+  class ForEachIterator
+  {
+  public:
+    virtual bool
+    URLParamsIterator(const nsString& aName, const nsString& aValue) = 0;
+  };
+
+  void
+  ParseInput(const nsACString& aInput);
+
+  bool
+  ForEach(ForEachIterator& aIterator) const
+  {
+    for (uint32_t i = 0; i < mParams.Length(); ++i) {
+      if (!aIterator.URLParamsIterator(mParams[i].mKey, mParams[i].mValue)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  void Serialize(nsAString& aValue) const;
+
+  void Get(const nsAString& aName, nsString& aRetval);
+
+  void GetAll(const nsAString& aName, nsTArray<nsString >& aRetval);
+
+  void Set(const nsAString& aName, const nsAString& aValue);
+
+  void Append(const nsAString& aName, const nsAString& aValue);
+
+  bool Has(const nsAString& aName);
+
+  // Returns true if aName was found and deleted, false otherwise.
+  bool Delete(const nsAString& aName);
+
+  void DeleteAll()
+  {
+    mParams.Clear();
+  }
+
+private:
+  void DecodeString(const nsACString& aInput, nsAString& aOutput);
+  void ConvertString(const nsACString& aInput, nsAString& aOutput);
+
+  struct Param
+  {
+    nsString mKey;
+    nsString mValue;
+  };
+
+  nsTArray<Param> mParams;
+  nsCOMPtr<nsIUnicodeDecoder> mDecoder;
+};
+
 class URLSearchParams final : public nsISupports,
                               public nsWrapperCache
 {
   ~URLSearchParams();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(URLSearchParams)
 
   explicit URLSearchParams(URLSearchParamsObserver* aObserver);
 
+  explicit URLSearchParams(const URLSearchParams& aOther);
+
   // WebIDL methods
   nsISupports* GetParentObject() const
   {
     return nullptr;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -60,69 +136,48 @@ public:
               ErrorResult& aRv);
 
   void ParseInput(const nsACString& aInput);
 
   void Serialize(nsAString& aValue) const;
 
   void Get(const nsAString& aName, nsString& aRetval);
 
-  void GetAll(const nsAString& aName, nsTArray<nsString >& aRetval);
+  void GetAll(const nsAString& aName, nsTArray<nsString>& aRetval);
 
   void Set(const nsAString& aName, const nsAString& aValue);
 
   void Append(const nsAString& aName, const nsAString& aValue);
 
   bool Has(const nsAString& aName);
 
   void Delete(const nsAString& aName);
 
   void Stringify(nsString& aRetval) const
   {
     Serialize(aRetval);
   }
 
-  class ForEachIterator
-  {
-  public:
-    virtual bool
-    URLSearchParamsIterator(const nsString& aName, const nsString& aValue) = 0;
-  };
+  typedef URLParams::ForEachIterator ForEachIterator;
 
   bool
-  ForEach(ForEachIterator& aIterator)
+  ForEach(ForEachIterator& aIterator) const
   {
-    for (uint32_t i = 0; i < mSearchParams.Length(); ++i) {
-      if (!aIterator.URLSearchParamsIterator(mSearchParams[i].mKey,
-                                             mSearchParams[i].mValue)) {
-        return false;
-      }
-    }
+    return mParams->ForEach(aIterator);
 
     return true;
   }
 
 private:
   void AppendInternal(const nsAString& aName, const nsAString& aValue);
 
   void DeleteAll();
 
-  void DecodeString(const nsACString& aInput, nsAString& aOutput);
-  void ConvertString(const nsACString& aInput, nsAString& aOutput);
-
   void NotifyObserver();
 
-  struct Param
-  {
-    nsString mKey;
-    nsString mValue;
-  };
-
-  nsTArray<Param> mSearchParams;
-
+  UniquePtr<URLParams> mParams;
   nsRefPtr<URLSearchParamsObserver> mObserver;
-  nsCOMPtr<nsIUnicodeDecoder> mDecoder;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_URLSearchParams_h */
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -496,18 +496,18 @@ class MOZ_STACK_CLASS FillFormIterator f
 {
 public:
   explicit FillFormIterator(nsFormData* aFormData)
     : mFormData(aFormData)
   {
     MOZ_ASSERT(aFormData);
   }
 
-  bool URLSearchParamsIterator(const nsString& aName,
-                               const nsString& aValue) override
+  bool URLParamsIterator(const nsString& aName,
+                         const nsString& aValue) override
   {
     mFormData->Append(aName, aValue);
     return true;
   }
 
 private:
   nsFormData* mFormData;
 };