Bug 1074963 - Constructor URLSearchParams() should preserve the order of pairs from passing argument, r=bz

This commit is contained in:
Andrea Marchesini 2014-10-01 14:55:33 +01:00
parent 026b6469d9
commit 5ecba97a38
3 changed files with 114 additions and 106 deletions

View File

@ -51,7 +51,7 @@ URLSearchParams::Constructor(const GlobalObject& aGlobal,
ErrorResult& aRv)
{
nsRefPtr<URLSearchParams> sp = new URLSearchParams();
aInit.mSearchParams.EnumerateRead(CopyEnumerator, sp);
sp->mSearchParams = aInit.mSearchParams;
return sp.forget();
}
@ -207,20 +207,6 @@ URLSearchParams::ConvertString(const nsACString& aInput, nsAString& aOutput)
}
}
/* static */ PLDHashOperator
URLSearchParams::CopyEnumerator(const nsAString& aName,
nsTArray<nsString>* aArray,
void *userData)
{
URLSearchParams* aSearchParams = static_cast<URLSearchParams*>(userData);
nsTArray<nsString>* newArray = new nsTArray<nsString>();
newArray->AppendElements(*aArray);
aSearchParams->mSearchParams.Put(aName, newArray);
return PL_DHASH_NEXT;
}
void
URLSearchParams::AddObserver(URLSearchParamsObserver* aObserver)
{
@ -245,38 +231,46 @@ URLSearchParams::RemoveObservers()
void
URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
{
nsTArray<nsString>* array;
if (!mSearchParams.Get(aName, &array)) {
aRetval.Truncate();
return;
}
aRetval.Truncate();
aRetval.Assign(array->ElementAt(0));
for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
if (mSearchParams[i].mKey.Equals(aName)) {
aRetval.Assign(mSearchParams[i].mValue);
break;
}
}
}
void
URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
{
nsTArray<nsString>* array;
if (!mSearchParams.Get(aName, &array)) {
return;
}
aRetval.Clear();
aRetval.AppendElements(*array);
for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
if (mSearchParams[i].mKey.Equals(aName)) {
aRetval.AppendElement(mSearchParams[i].mValue);
}
}
}
void
URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
{
nsTArray<nsString>* array;
if (!mSearchParams.Get(aName, &array)) {
array = new nsTArray<nsString>();
array->AppendElement(aValue);
mSearchParams.Put(aName, array);
} else {
array->ElementAt(0) = aValue;
Param* param = nullptr;
for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
if (mSearchParams[i].mKey.Equals(aName)) {
param = &mSearchParams[i];
break;
}
}
if (!param) {
param = mSearchParams.AppendElement();
param->mKey = aName;
}
param->mValue = aValue;
NotifyObservers(nullptr);
}
@ -290,32 +284,39 @@ URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
void
URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue)
{
nsTArray<nsString>* array;
if (!mSearchParams.Get(aName, &array)) {
array = new nsTArray<nsString>();
mSearchParams.Put(aName, array);
}
array->AppendElement(aValue);
Param* param = mSearchParams.AppendElement();
param->mKey = aName;
param->mValue = aValue;
}
bool
URLSearchParams::Has(const nsAString& aName)
{
return mSearchParams.Get(aName, nullptr);
for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
if (mSearchParams[i].mKey.Equals(aName)) {
return true;
}
}
return false;
}
void
URLSearchParams::Delete(const nsAString& aName)
{
nsTArray<nsString>* array;
if (!mSearchParams.Get(aName, &array)) {
return;
bool found = false;
for (uint32_t i = 0; i < mSearchParams.Length();) {
if (mSearchParams[i].mKey.Equals(aName)) {
mSearchParams.RemoveElementAt(i);
found = true;
} else {
++i;
}
}
mSearchParams.Remove(aName);
NotifyObservers(nullptr);
if (found) {
NotifyObservers(nullptr);
}
}
void
@ -324,67 +325,49 @@ URLSearchParams::DeleteAll()
mSearchParams.Clear();
}
class MOZ_STACK_CLASS SerializeData
namespace {
void SerializeString(const nsCString& aInput, nsAString& aValue)
{
public:
SerializeData()
: mFirst(true)
{}
const unsigned char* p = (const unsigned char*) aInput.get();
nsAutoString mValue;
bool mFirst;
void Serialize(const nsCString& aInput)
{
const unsigned char* p = (const unsigned char*) aInput.get();
while (p && *p) {
// ' ' to '+'
if (*p == 0x20) {
mValue.Append(0x2B);
// Percent Encode algorithm
} else if (*p == 0x2A || *p == 0x2D || *p == 0x2E ||
(*p >= 0x30 && *p <= 0x39) ||
(*p >= 0x41 && *p <= 0x5A) || *p == 0x5F ||
(*p >= 0x61 && *p <= 0x7A)) {
mValue.Append(*p);
} else {
mValue.AppendPrintf("%%%.2X", *p);
}
++p;
while (p && *p) {
// ' ' to '+'
if (*p == 0x20) {
aValue.Append(0x2B);
// Percent Encode algorithm
} else if (*p == 0x2A || *p == 0x2D || *p == 0x2E ||
(*p >= 0x30 && *p <= 0x39) ||
(*p >= 0x41 && *p <= 0x5A) || *p == 0x5F ||
(*p >= 0x61 && *p <= 0x7A)) {
aValue.Append(*p);
} else {
aValue.AppendPrintf("%%%.2X", *p);
}
++p;
}
};
}
} // anonymous namespace
void
URLSearchParams::Serialize(nsAString& aValue) const
{
SerializeData data;
mSearchParams.EnumerateRead(SerializeEnumerator, &data);
aValue.Assign(data.mValue);
}
aValue.Truncate();
bool first = true;
/* static */ PLDHashOperator
URLSearchParams::SerializeEnumerator(const nsAString& aName,
nsTArray<nsString>* aArray,
void *userData)
{
SerializeData* data = static_cast<SerializeData*>(userData);
for (uint32_t i = 0, len = aArray->Length(); i < len; ++i) {
if (data->mFirst) {
data->mFirst = false;
for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
if (first) {
first = false;
} else {
data->mValue.Append('&');
aValue.Append('&');
}
data->Serialize(NS_ConvertUTF16toUTF8(aName));
data->mValue.Append('=');
data->Serialize(NS_ConvertUTF16toUTF8(aArray->ElementAt(i)));
SerializeString(NS_ConvertUTF16toUTF8(mSearchParams[i].mKey), aValue);
aValue.Append('=');
SerializeString(NS_ConvertUTF16toUTF8(mSearchParams[i].mValue), aValue);
}
return PL_DHASH_NEXT;
}
void

View File

@ -10,8 +10,6 @@
#include "mozilla/ErrorResult.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "nsISupports.h"
#include "nsIUnicodeDecoder.h"
@ -92,15 +90,13 @@ private:
void NotifyObservers(URLSearchParamsObserver* aExceptObserver);
static PLDHashOperator
CopyEnumerator(const nsAString& aName, nsTArray<nsString>* aArray,
void *userData);
struct Param
{
nsString mKey;
nsString mValue;
};
static PLDHashOperator
SerializeEnumerator(const nsAString& aName, nsTArray<nsString>* aArray,
void *userData);
nsClassHashtable<nsStringHashKey, nsTArray<nsString>> mSearchParams;
nsTArray<Param> mSearchParams;
nsTArray<nsRefPtr<URLSearchParamsObserver>> mObservers;
nsCOMPtr<nsIUnicodeDecoder> mDecoder;

View File

@ -248,6 +248,33 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836
runTest();
}
function testOrdering() {
var a = new URLSearchParams("a=1&a=2&b=3&c=4&c=5&a=6");
is(a.toString(), "a=1&a=2&b=3&c=4&c=5&a=6", "Order is correct");
is(a.getAll('a').length, 3, "Correct length of getAll()");
var b = new URLSearchParams();
b.append('a', '1');
b.append('b', '2');
b.append('a', '3');
is(b.toString(), "a=1&b=2&a=3", "Order is correct");
is(b.getAll('a').length, 2, "Correct length of getAll()");
runTest();
}
function testDelete() {
var a = new URLSearchParams("a=1&a=2&b=3&c=4&c=5&a=6");
is(a.toString(), "a=1&a=2&b=3&c=4&c=5&a=6", "Order is correct");
is(a.getAll('a').length, 3, "Correct length of getAll()");
a.delete('a');
is(a.getAll('a').length, 0, "Correct length of getAll()");
is(a.toString(), "b=3&c=4&c=5", "Order is correct");
runTest();
}
var tests = [
testSimpleURLSearchParams,
testCopyURLSearchParams,
@ -256,7 +283,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836
function() { testElement(document.getElementById('anchor')) },
function() { testElement(document.getElementById('area')) },
testEncoding,
testMultiURL
testMultiURL,
testOrdering,
testDelete
];
function runTest() {