Backed out changeset 2d021370c57f (bug 965990) for causing bug 973307.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 21 Feb 2014 16:54:42 -0500
changeset 170327 ae91046b210ac57e984b7f9090e5519e1719a2bb
parent 170326 e9f9f941a9a72bcc0cb76d511383bfae0984622c
child 170328 21bbea0bdc12ebeaf9e2751c816de8418b13f41c
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
bugs965990, 973307
milestone30.0a1
backs out2d021370c57fd3b73d74e989a03b021530232caf
Backed out changeset 2d021370c57f (bug 965990) for causing bug 973307.
content/base/src/Link.cpp
content/base/src/Link.h
dom/base/URL.cpp
dom/base/URL.h
dom/base/URLSearchParams.cpp
dom/base/URLSearchParams.h
dom/base/test/test_urlSearchParams.html
dom/workers/URL.cpp
dom/workers/URL.h
dom/workers/test/urlSearchParams_worker.js
toolkit/components/places/tests/cpp/mock_Link.h
--- a/content/base/src/Link.cpp
+++ b/content/base/src/Link.cpp
@@ -212,17 +212,19 @@ Link::SetPathname(const nsAString &aPath
   (void)url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
   SetHrefAttribute(uri);
 }
 
 void
 Link::SetSearch(const nsAString& aSearch)
 {
   SetSearchInternal(aSearch);
-  UpdateURLSearchParams();
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 }
 
 void
 Link::SetSearchInternal(const nsAString& aSearch)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (!url) {
@@ -480,17 +482,19 @@ Link::ResetLinkState(bool aNotify, bool 
     UnregisterFromHistory();
   }
 
   // If we have an href, we should register with the history.
   mNeedsRegistration = aHasHref;
 
   // If we've cached the URI, reset always invalidates it.
   mCachedURI = nullptr;
-  UpdateURLSearchParams();
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 
   // Update our state back to the default.
   mLinkState = defaultState;
 
   // We have to be very careful here: if aNotify is false we do NOT
   // want to call UpdateState, because that will call into LinkState()
   // and try to start off loads, etc.  But ResetLinkState is called
   // with aNotify false when things are in inconsistent states, so
@@ -580,65 +584,66 @@ Link::GetSearchParams()
 
 void
 Link::SetSearchParams(URLSearchParams* aSearchParams)
 {
   if (!aSearchParams) {
     return;
   }
 
-  if (mSearchParams) {
-    mSearchParams->RemoveObserver(this);
+  if (!aSearchParams->HasURLAssociated()) {
+    MOZ_ASSERT(aSearchParams->IsValid());
+
+    mSearchParams = aSearchParams;
+    mSearchParams->SetObserver(this);
+  } else {
+    CreateSearchParamsIfNeeded();
+    mSearchParams->CopyFromURLSearchParams(*aSearchParams);
   }
 
-  mSearchParams = aSearchParams;
-  mSearchParams->AddObserver(this);
-
   nsAutoString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
 Link::URLSearchParamsUpdated()
 {
-  MOZ_ASSERT(mSearchParams);
+  MOZ_ASSERT(mSearchParams && mSearchParams->IsValid());
 
   nsString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
-Link::UpdateURLSearchParams()
+Link::URLSearchParamsNeedsUpdates()
 {
-  if (!mSearchParams) {
-    return;
-  }
+  MOZ_ASSERT(mSearchParams);
 
   nsAutoCString search;
   nsCOMPtr<nsIURI> uri(GetURI());
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (url) {
     nsresult rv = url->GetQuery(search);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to get the query from a nsIURL.");
     }
   }
 
-  mSearchParams->ParseInput(search, this);
+  mSearchParams->ParseInput(search);
 }
 
 void
 Link::CreateSearchParamsIfNeeded()
 {
   if (!mSearchParams) {
     mSearchParams = new URLSearchParams();
-    mSearchParams->AddObserver(this);
-    UpdateURLSearchParams();
+    mSearchParams->SetObserver(this);
+    mSearchParams->Invalidate();
   }
 }
 
 void
 Link::Unlink()
 {
   mSearchParams = nullptr;
 }
--- a/content/base/src/Link.h
+++ b/content/base/src/Link.h
@@ -109,16 +109,17 @@ public:
 
   virtual size_t
     SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   bool ElementHasHref() const;
 
   // URLSearchParamsObserver
   void URLSearchParamsUpdated() MOZ_OVERRIDE;
+  void URLSearchParamsNeedsUpdates() MOZ_OVERRIDE;
 
 protected:
   virtual ~Link();
 
   /**
    * Return true if the link has associated URI.
    */
   bool HasURI() const
@@ -128,18 +129,16 @@ protected:
     }
 
     return !!GetURI();
   }
 
   nsIURI* GetCachedURI() const { return mCachedURI; }
   bool HasCachedURI() const { return !!mCachedURI; }
 
-  void UpdateURLSearchParams();
-
   // CC methods
   void Unlink();
   void Traverse(nsCycleCollectionTraversalCallback &cb);
 
 private:
   /**
    * Unregisters from History so this node no longer gets notifications about
    * changes to visitedness.
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -216,17 +216,20 @@ URL::SetHref(const nsAString& aHref, Err
   rv = ioService->NewURI(href, nullptr, nullptr, getter_AddRefs(uri));
   if (NS_FAILED(rv)) {
     nsAutoString label(aHref);
     aRv.ThrowTypeError(MSG_INVALID_URL, &label);
     return;
   }
 
   aRv = mURI->SetSpec(href);
-  UpdateURLSearchParams();
+
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 }
 
 void
 URL::GetOrigin(nsString& aOrigin) const
 {
   nsContentUtils::GetUTFNonNullOrigin(mURI, aOrigin);
 }
 
@@ -296,40 +299,38 @@ void
 URL::SetHost(const nsAString& aHost)
 {
   mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
 }
 
 void
 URL::URLSearchParamsUpdated()
 {
-  MOZ_ASSERT(mSearchParams);
+  MOZ_ASSERT(mSearchParams && mSearchParams->IsValid());
 
   nsAutoString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
-URL::UpdateURLSearchParams()
+URL::URLSearchParamsNeedsUpdates()
 {
-  if (!mSearchParams) {
-    return;
-  }
+  MOZ_ASSERT(mSearchParams);
 
   nsAutoCString search;
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (url) {
     nsresult rv = url->GetQuery(search);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to get the query from a nsIURL.");
     }
   }
 
-  mSearchParams->ParseInput(search, this);
+  mSearchParams->ParseInput(search);
 }
 
 void
 URL::GetHostname(nsString& aHostname) const
 {
   URL_GETTER(aHostname, GetHost);
 }
 
@@ -420,17 +421,20 @@ URL::GetSearch(nsString& aSearch) const
     CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
   }
 }
 
 void
 URL::SetSearch(const nsAString& aSearch)
 {
   SetSearchInternal(aSearch);
-  UpdateURLSearchParams();
+
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 }
 
 void
 URL::SetSearchInternal(const nsAString& aSearch)
 {
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (!url) {
     // Ignore failures to be compatible with NS4.
@@ -449,23 +453,25 @@ URL::GetSearchParams()
 
 void
 URL::SetSearchParams(URLSearchParams* aSearchParams)
 {
   if (!aSearchParams) {
     return;
   }
 
-  if (mSearchParams) {
-    mSearchParams->RemoveObserver(this);
-  }
+  if (!aSearchParams->HasURLAssociated()) {
+    MOZ_ASSERT(aSearchParams->IsValid());
 
-  // the observer will be cleared using the cycle collector.
-  mSearchParams = aSearchParams;
-  mSearchParams->AddObserver(this);
+    mSearchParams = aSearchParams;
+    mSearchParams->SetObserver(this);
+  } else {
+    CreateSearchParamsIfNeeded();
+    mSearchParams->CopyFromURLSearchParams(*aSearchParams);
+  }
 
   nsAutoString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
 URL::GetHash(nsString& aHash) const
@@ -495,15 +501,15 @@ bool IsChromeURI(nsIURI* aURI)
   return false;
 }
 
 void
 URL::CreateSearchParamsIfNeeded()
 {
   if (!mSearchParams) {
     mSearchParams = new URLSearchParams();
-    mSearchParams->AddObserver(this);
-    UpdateURLSearchParams();
+    mSearchParams->SetObserver(this);
+    mSearchParams->Invalidate();
   }
 }
 
 }
 }
--- a/dom/base/URL.h
+++ b/dom/base/URL.h
@@ -120,29 +120,28 @@ public:
 
   void Stringify(nsString& aRetval) const
   {
     GetHref(aRetval);
   }
 
   // URLSearchParamsObserver
   void URLSearchParamsUpdated() MOZ_OVERRIDE;
+  void URLSearchParamsNeedsUpdates() MOZ_OVERRIDE;
 
 private:
   nsIURI* GetURI() const
   {
     return mURI;
   }
 
   void CreateSearchParamsIfNeeded();
 
   void SetSearchInternal(const nsAString& aSearch);
 
-  void UpdateURLSearchParams();
-
   static void CreateObjectURLInternal(const GlobalObject& aGlobal,
                                       nsISupports* aObject,
                                       const nsACString& aScheme,
                                       const objectURLOptions& aOptions,
                                       nsString& aResult,
                                       ErrorResult& aError);
 
   nsCOMPtr<nsIURI> mURI;
--- a/dom/base/URLSearchParams.cpp
+++ b/dom/base/URLSearchParams.cpp
@@ -4,26 +4,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "URLSearchParams.h"
 #include "mozilla/dom/URLSearchParamsBinding.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(URLSearchParams, mObservers)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(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()
+  : mValid(false)
 {
   SetIsDOMBinding();
 }
 
 URLSearchParams::~URLSearchParams()
 {
   DeleteAll();
 }
@@ -35,33 +36,33 @@ URLSearchParams::WrapObject(JSContext* a
 }
 
 /* static */ already_AddRefed<URLSearchParams>
 URLSearchParams::Constructor(const GlobalObject& aGlobal,
                              const nsAString& aInit,
                              ErrorResult& aRv)
 {
   nsRefPtr<URLSearchParams> sp = new URLSearchParams();
-  sp->ParseInput(NS_ConvertUTF16toUTF8(aInit), 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.mSearchParams.EnumerateRead(CopyEnumerator, sp);
+  sp->mValid = true;
   return sp.forget();
 }
 
 void
-URLSearchParams::ParseInput(const nsACString& aInput,
-                            URLSearchParamsObserver* aObserver)
+URLSearchParams::ParseInput(const nsACString& aInput)
 {
   // 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);
@@ -103,17 +104,17 @@ URLSearchParams::ParseInput(const nsACSt
 
     nsAutoCString decodedValue;
     DecodeString(value, decodedValue);
 
     AppendInternal(NS_ConvertUTF8toUTF16(decodedName),
                    NS_ConvertUTF8toUTF16(decodedValue));
   }
 
-  NotifyObservers(aObserver);
+  mValid = true;
 }
 
 void
 URLSearchParams::DecodeString(const nsACString& aInput, nsACString& aOutput)
 {
   nsACString::const_iterator start, end;
   aInput.BeginReading(start);
   aInput.EndReading(end);
@@ -159,89 +160,114 @@ URLSearchParams::DecodeString(const nsAC
       }
     }
 
     aOutput.Append(*start);
     ++start;
   }
 }
 
+void
+URLSearchParams::CopyFromURLSearchParams(URLSearchParams& aSearchParams)
+{
+  // The other SearchParams must be valid before copying its data.
+  aSearchParams.Validate();
+
+  // Remove all the existing data before parsing a new input.
+  DeleteAll();
+  aSearchParams.mSearchParams.EnumerateRead(CopyEnumerator, this);
+  mValid = true;
+}
+
 /* 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)
+URLSearchParams::SetObserver(URLSearchParamsObserver* aObserver)
 {
-  MOZ_ASSERT(aObserver);
-  MOZ_ASSERT(!mObservers.Contains(aObserver));
-  mObservers.AppendElement(aObserver);
+  MOZ_ASSERT(!mObserver);
+  mObserver = aObserver;
 }
 
 void
-URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver)
+URLSearchParams::Validate()
 {
-  MOZ_ASSERT(aObserver);
-  MOZ_ASSERT(mObservers.Contains(aObserver));
-  mObservers.RemoveElement(aObserver);
+  MOZ_ASSERT(mValid || mObserver);
+  if (!mValid) {
+    mObserver->URLSearchParamsNeedsUpdates();
+    MOZ_ASSERT(mValid);
+  }
 }
 
 void
 URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
 {
+  Validate();
+
   nsTArray<nsString>* array;
   if (!mSearchParams.Get(aName, &array)) {
     aRetval.Truncate();
     return;
   }
 
   aRetval.Assign(array->ElementAt(0));
 }
 
 void
 URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
 {
+  Validate();
+
   nsTArray<nsString>* array;
   if (!mSearchParams.Get(aName, &array)) {
     return;
   }
 
   aRetval.AppendElements(*array);
 }
 
 void
 URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
 {
+  // Before setting any new value we have to be sure to have all the previous
+  // values in place.
+  Validate();
+
   nsTArray<nsString>* array;
   if (!mSearchParams.Get(aName, &array)) {
     array = new nsTArray<nsString>();
     array->AppendElement(aValue);
     mSearchParams.Put(aName, array);
   } else {
     array->ElementAt(0) = aValue;
   }
 
-  NotifyObservers(nullptr);
+  NotifyObserver();
 }
 
 void
 URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
 {
+  // Before setting any new value we have to be sure to have all the previous
+  // values in place.
+  Validate();
+
   AppendInternal(aName, aValue);
-  NotifyObservers(nullptr);
+  NotifyObserver();
 }
 
 void
 URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue)
 {
   nsTArray<nsString>* array;
   if (!mSearchParams.Get(aName, &array)) {
     array = new nsTArray<nsString>();
@@ -249,30 +275,35 @@ URLSearchParams::AppendInternal(const ns
   }
 
   array->AppendElement(aValue);
 }
 
 bool
 URLSearchParams::Has(const nsAString& aName)
 {
+  Validate();
   return mSearchParams.Get(aName, nullptr);
 }
 
 void
 URLSearchParams::Delete(const nsAString& aName)
 {
+  // Before deleting any value we have to be sure to have all the previous
+  // values in place.
+  Validate();
+
   nsTArray<nsString>* array;
   if (!mSearchParams.Get(aName, &array)) {
     return;
   }
 
   mSearchParams.Remove(aName);
 
-  NotifyObservers(nullptr);
+  NotifyObserver();
 }
 
 void
 URLSearchParams::DeleteAll()
 {
   mSearchParams.Clear();
 }
 
@@ -307,16 +338,18 @@ public:
       ++p;
     }
   }
 };
 
 void
 URLSearchParams::Serialize(nsAString& aValue) const
 {
+  MOZ_ASSERT(mValid);
+
   SerializeData data;
   mSearchParams.EnumerateRead(SerializeEnumerator, &data);
   aValue.Assign(data.mValue);
 }
 
 /* static */ PLDHashOperator
 URLSearchParams::SerializeEnumerator(const nsAString& aName,
                                      nsTArray<nsString>* aArray,
@@ -335,19 +368,23 @@ URLSearchParams::SerializeEnumerator(con
     data->mValue.Append(NS_LITERAL_STRING("="));
     data->Serialize(NS_ConvertUTF16toUTF8(aArray->ElementAt(i)));
   }
 
   return PL_DHASH_NEXT;
 }
 
 void
-URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver)
+URLSearchParams::NotifyObserver()
 {
-  for (uint32_t i = 0; i < mObservers.Length(); ++i) {
-    if (mObservers[i] != aExceptObserver) {
-      mObservers[i]->URLSearchParamsUpdated();
-    }
+  if (mObserver) {
+    mObserver->URLSearchParamsUpdated();
   }
 }
 
+void
+URLSearchParams::Invalidate()
+{
+  mValid = false;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/URLSearchParams.h
+++ b/dom/base/URLSearchParams.h
@@ -18,28 +18,34 @@ namespace mozilla {
 namespace dom {
 
 class URLSearchParamsObserver : public nsISupports
 {
 public:
   virtual ~URLSearchParamsObserver() {}
 
   virtual void URLSearchParamsUpdated() = 0;
+  virtual void URLSearchParamsNeedsUpdates() = 0;
 };
 
 class URLSearchParams MOZ_FINAL : public nsISupports,
                                   public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(URLSearchParams)
 
   URLSearchParams();
   ~URLSearchParams();
 
+  bool HasURLAssociated() const
+  {
+    return !!mObserver;
+  }
+
   // WebIDL methods
   nsISupports* GetParentObject() const
   {
     return nullptr;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
@@ -47,21 +53,28 @@ public:
   static already_AddRefed<URLSearchParams>
   Constructor(const GlobalObject& aGlobal, const nsAString& aInit,
               ErrorResult& aRv);
 
   static already_AddRefed<URLSearchParams>
   Constructor(const GlobalObject& aGlobal, URLSearchParams& aInit,
               ErrorResult& aRv);
 
-  void ParseInput(const nsACString& aInput,
-                  URLSearchParamsObserver* aObserver);
+  void ParseInput(const nsACString& aInput);
+
+  void CopyFromURLSearchParams(URLSearchParams& aSearchParams);
+
+  void SetObserver(URLSearchParamsObserver* aObserver);
 
-  void AddObserver(URLSearchParamsObserver* aObserver);
-  void RemoveObserver(URLSearchParamsObserver* aObserver);
+  void Invalidate();
+
+  bool IsValid() const
+  {
+    return mValid;
+  }
 
   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);
@@ -69,37 +82,43 @@ public:
   void Append(const nsAString& aName, const nsAString& aValue);
 
   bool Has(const nsAString& aName);
 
   void Delete(const nsAString& aName);
 
   void Stringify(nsString& aRetval)
   {
+    Validate();
     Serialize(aRetval);
   }
 
 private:
   void AppendInternal(const nsAString& aName, const nsAString& aValue);
 
   void DeleteAll();
 
   void DecodeString(const nsACString& aInput, nsACString& aOutput);
 
-  void NotifyObservers(URLSearchParamsObserver* aExceptObserver);
+  void NotifyObserver();
 
   static PLDHashOperator
   CopyEnumerator(const nsAString& aName, nsTArray<nsString>* aArray,
                  void *userData);
 
   static PLDHashOperator
   SerializeEnumerator(const nsAString& aName, nsTArray<nsString>* aArray,
                       void *userData);
 
+  void
+  Validate();
+
   nsClassHashtable<nsStringHashKey, nsTArray<nsString>> mSearchParams;
 
-  nsTArray<nsRefPtr<URLSearchParamsObserver>> mObservers;
+  nsRefPtr<URLSearchParamsObserver> mObserver;
+
+  bool mValid;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_URLSearchParams_h */
--- a/dom/base/test/test_urlSearchParams.html
+++ b/dom/base/test/test_urlSearchParams.html
@@ -132,17 +132,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
 
     url.searchParams = null;
     is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
     is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
 
     var url2 = new URL('http://www.example.net?e=f');
     url.searchParams = url2.searchParams;
-    is(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
+    isnot(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
     is(url.searchParams.get('e'), 'f', "URL.searchParams.get('e')");
 
     url.href = "http://www.example.net?bar=foo";
     is(url.searchParams.get('bar'), 'foo', "URL.searchParams.get('bar')");
 
     runTest();
   }
 
@@ -165,17 +165,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(e.href, 'http://www.example.net/?foo=bar', 'e is right');
 
     e.searchParams = null;
     is(e.searchParams.get('foo'), 'bar', "e.searchParams.get('foo')");
     is(e.href, 'http://www.example.net/?foo=bar', 'e is right');
 
     var url2 = new URL('http://www.example.net?e=f');
     e.searchParams = url2.searchParams;
-    is(e.searchParams, url2.searchParams, "e.searchParams is not the same object");
+    isnot(e.searchParams, url2.searchParams, "e.searchParams is not the same object");
     is(e.searchParams.get('e'), 'f', "e.searchParams.get('e')");
 
     e.href = "http://www.example.net?bar=foo";
     is(e.searchParams.get('bar'), 'foo', "e.searchParams.get('bar')");
 
     e.setAttribute('href', "http://www.example.net?bar2=foo2");
     is(e.searchParams.get('bar2'), 'foo2', "e.searchParams.get('bar2')");
 
@@ -198,64 +198,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 
       var url2 = new URL(url.href);
       is(url2.searchParams.get('a'), encoding[i][0], 'a is still there');
     }
 
     runTest();
   }
 
-  function testMultiURL() {
-    var a = new URL('http://www.example.net?a=b&c=d');
-    var b = new URL('http://www.example.net?e=f');
-    var c = document.createElement('a');
-    var d = document.createElement('area');
-    ok(a.searchParams.has('a'), "a.searchParams.has('a')");
-    ok(a.searchParams.has('c'), "a.searchParams.has('c')");
-    ok(b.searchParams.has('e'), "b.searchParams.has('e')");
-    ok(c.searchParams, "c.searchParams");
-    ok(d.searchParams, "d.searchParams");
-
-    var u = new URLSearchParams();
-    a.searchParams = b.searchParams = c.searchParams = d.searchParams = u;
-    is(a.searchParams, u, "a.searchParams === u");
-    is(b.searchParams, u, "b.searchParams === u");
-    is(c.searchParams, u, "c.searchParams === u");
-    is(d.searchParams, u, "d.searchParams === u");
-    ok(!a.searchParams.has('a'), "!a.searchParams.has('a')");
-    ok(!a.searchParams.has('c'), "!a.searchParams.has('c')");
-    ok(!b.searchParams.has('e'), "!b.searchParams.has('e')");
-
-    u.append('foo', 'bar');
-    is(a.searchParams.get('foo'), 'bar', "a has foo=bar");
-    is(b.searchParams.get('foo'), 'bar', "b has foo=bar");
-    is(c.searchParams.get('foo'), 'bar', "c has foo=bar");
-    is(d.searchParams.get('foo'), 'bar', "d has foo=bar");
-    is(a + "", b + "", "stringify a == b");
-    is(c.searchParams + "", b.searchParams + "", "stringify c.searchParams == b.searchParams");
-    is(d.searchParams + "", b.searchParams + "", "stringify d.searchParams == b.searchParams");
-
-    a.search = "?bar=foo";
-    is(a.searchParams.get('bar'), 'foo', "a has bar=foo");
-    is(b.searchParams.get('bar'), 'foo', "b has bar=foo");
-    is(c.searchParams.get('bar'), 'foo', "c has bar=foo");
-    is(d.searchParams.get('bar'), 'foo', "d has bar=foo");
-
-    runTest();
-  }
-
   var tests = [
     testSimpleURLSearchParams,
     testCopyURLSearchParams,
     testParserURLSearchParams,
     testURL,
     function() { testElement(document.getElementById('anchor')) },
     function() { testElement(document.getElementById('area')) },
-    testEncoding,
-    testMultiURL
+    testEncoding
   ];
 
   function runTest() {
     if (!tests.length) {
       SimpleTest.finish();
       return;
     }
 
--- a/dom/workers/URL.cpp
+++ b/dom/workers/URL.cpp
@@ -610,17 +610,19 @@ URL::SetHref(const nsAString& aHref, Err
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHref, aHref,
                        mURLProxy, aRv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 
-  UpdateURLSearchParams();
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 }
 
 void
 URL::GetOrigin(nsString& aOrigin) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterOrigin, aOrigin,
                        mURLProxy);
@@ -816,17 +818,20 @@ URL::GetSearch(nsString& aSearch) const
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
 URL::SetSearch(const nsAString& aSearch)
 {
   SetSearchInternal(aSearch);
-  UpdateURLSearchParams();
+
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 }
 
 void
 URL::SetSearchInternal(const nsAString& aSearch)
 {
   ErrorResult rv;
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterSearch,
@@ -846,22 +851,26 @@ URL::GetSearchParams()
 
 void
 URL::SetSearchParams(URLSearchParams* aSearchParams)
 {
   if (!aSearchParams) {
     return;
   }
 
-  if (mSearchParams) {
-    mSearchParams->RemoveObserver(this);
+  if (!aSearchParams->HasURLAssociated()) {
+    MOZ_ASSERT(aSearchParams->IsValid());
+
+    mSearchParams = aSearchParams;
+    mSearchParams->SetObserver(this);
+  } else {
+    CreateSearchParamsIfNeeded();
+    mSearchParams->CopyFromURLSearchParams(*aSearchParams);
   }
 
-  mSearchParams = aSearchParams;
-  mSearchParams->AddObserver(this);
 
   nsString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
 URL::GetHash(nsString& aHash) const
@@ -937,36 +946,36 @@ URL::RevokeObjectURL(const GlobalObject&
   if (!runnable->Dispatch(cx)) {
     JS_ReportPendingException(cx);
   }
 }
 
 void
 URL::URLSearchParamsUpdated()
 {
-  MOZ_ASSERT(mSearchParams);
+  MOZ_ASSERT(mSearchParams && mSearchParams->IsValid());
 
   nsString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
-URL::UpdateURLSearchParams()
+URL::URLSearchParamsNeedsUpdates()
 {
-  if (mSearchParams) {
-    nsString search;
-    GetSearch(search);
-    mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1)), this);
-  }
+  MOZ_ASSERT(mSearchParams);
+
+  nsString search;
+  GetSearch(search);
+  mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1)));
 }
 
 void
 URL::CreateSearchParamsIfNeeded()
 {
   if (!mSearchParams) {
     mSearchParams = new URLSearchParams();
-    mSearchParams->AddObserver(this);
-    UpdateURLSearchParams();
+    mSearchParams->SetObserver(this);
+    mSearchParams->Invalidate();
   }
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/URL.h
+++ b/dom/workers/URL.h
@@ -114,29 +114,28 @@ public:
 
   void Stringify(nsString& aRetval) const
   {
     GetHref(aRetval);
   }
 
   // IURLSearchParamsObserver
   void URLSearchParamsUpdated() MOZ_OVERRIDE;
+  void URLSearchParamsNeedsUpdates() MOZ_OVERRIDE;
 
 private:
   URLProxy* GetURLProxy() const
   {
     return mURLProxy;
   }
 
   void CreateSearchParamsIfNeeded();
 
   void SetSearchInternal(const nsAString& aSearch);
 
-  void UpdateURLSearchParams();
-
   WorkerPrivate* mWorkerPrivate;
   nsRefPtr<URLProxy> mURLProxy;
   nsRefPtr<URLSearchParams> mSearchParams;
 };
 
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_url_h__ */
--- a/dom/workers/test/urlSearchParams_worker.js
+++ b/dom/workers/test/urlSearchParams_worker.js
@@ -3,16 +3,21 @@ function ok(a, msg) {
   postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
 }
 
 function is(a, b, msg) {
   dump("IS: " + (a===b) + "  =>  " + a + " | " + b + " " + msg + "\n");
   postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
 }
 
+function isnot(a, b, msg) {
+  dump("ISNOT: " + (a!==b) + "  =>  " + a + " | " + b + " " + msg + "\n");
+  postMessage({type: 'status', status: a !== b, msg: a + " !== " + b + ": " + msg });
+}
+
 onmessage = function() {
   status = false;
   try {
     if ((URLSearchParams instanceof Object)) {
       status = true;
     }
   } catch(e) {
   }
@@ -124,17 +129,17 @@ onmessage = function() {
     is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
 
     url.searchParams = null;
     is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
     is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
 
     var url2 = new URL('http://www.example.net?e=f');
     url.searchParams = url2.searchParams;
-    is(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
+    isnot(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
     is(url.searchParams.get('e'), 'f', "URL.searchParams.get('e')");
 
     url.href = "http://www.example.net?bar=foo";
     is(url.searchParams.get('bar'), 'foo', "URL.searchParams.get('bar')");
 
     runTest();
   }
 
@@ -154,49 +159,22 @@ onmessage = function() {
 
       var url2 = new URL(url.href);
       is(url2.searchParams.get('a'), encoding[i][0], 'a is still there');
     }
 
     runTest();
   }
 
-  function testMultiURL() {
-    var a = new URL('http://www.example.net?a=b&c=d');
-    var b = new URL('http://www.example.net?e=f');
-    ok(a.searchParams.has('a'), "a.searchParams.has('a')");
-    ok(a.searchParams.has('c'), "a.searchParams.has('c')");
-    ok(b.searchParams.has('e'), "b.searchParams.has('e')");
-
-    var u = new URLSearchParams();
-    a.searchParams = b.searchParams = u;
-    is(a.searchParams, u, "a.searchParams === u");
-    is(b.searchParams, u, "b.searchParams === u");
-    ok(!a.searchParams.has('a'), "!a.searchParams.has('a')");
-    ok(!a.searchParams.has('c'), "!a.searchParams.has('c')");
-    ok(!b.searchParams.has('e'), "!b.searchParams.has('e')");
-
-    u.append('foo', 'bar');
-    is(a.searchParams.get('foo'), 'bar', "a has foo=bar");
-    is(b.searchParams.get('foo'), 'bar', "b has foo=bar");
-    is(a + "", b + "", "stringify a == b");
-
-    a.search = "?bar=foo";
-    is(a.searchParams.get('bar'), 'foo', "a has bar=foo");
-    is(b.searchParams.get('bar'), 'foo', "b has bar=foo");
-
-    runTest();
-  }
   var tests = [
     testSimpleURLSearchParams,
     testCopyURLSearchParams,
     testParserURLSearchParams,
     testURL,
-    testEncoding,
-    testMultiURL
+    testEncoding
   ];
 
   function runTest() {
     if (!tests.length) {
       postMessage({type: 'finish' });
       return;
     }
 
--- a/toolkit/components/places/tests/cpp/mock_Link.h
+++ b/toolkit/components/places/tests/cpp/mock_Link.h
@@ -118,19 +118,19 @@ Link::SizeOfExcludingThis(mozilla::Mallo
 
 void
 Link::URLSearchParamsUpdated()
 {
   NS_NOTREACHED("Unexpected call to Link::URLSearchParamsUpdated");
 }
 
 void
-Link::UpdateURLSearchParams()
+Link::URLSearchParamsNeedsUpdates()
 {
-  NS_NOTREACHED("Unexpected call to Link::UpdateURLSearchParams");
+  NS_NOTREACHED("Unexpected call to Link::URLSearchParamsNeedsUpdates");
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(URLSearchParams)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(URLSearchParams)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(URLSearchParams)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(URLSearchParams)
@@ -154,30 +154,29 @@ URLSearchParams::~URLSearchParams()
 
 JSObject*
 URLSearchParams::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return nullptr;
 }
 
 void
-URLSearchParams::ParseInput(const nsACString& aInput,
-                            URLSearchParamsObserver* aObserver)
+URLSearchParams::ParseInput(const nsACString& aInput)
 {
   NS_NOTREACHED("Unexpected call to URLSearchParams::ParseInput");
 }
 
 void
-URLSearchParams::AddObserver(URLSearchParamsObserver* aObserver)
+URLSearchParams::CopyFromURLSearchParams(URLSearchParams& aSearchParams)
 {
-  NS_NOTREACHED("Unexpected call to URLSearchParams::SetObserver");
+  NS_NOTREACHED("Unexpected call to URLSearchParams::CopyFromURLSearchParams");
 }
 
 void
-URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver)
+URLSearchParams::SetObserver(URLSearchParamsObserver* aObserver)
 {
   NS_NOTREACHED("Unexpected call to URLSearchParams::SetObserver");
 }
 
 void
 URLSearchParams::Serialize(nsAString& aValue) const
 {
   NS_NOTREACHED("Unexpected call to URLSearchParams::Serialize");
@@ -228,17 +227,24 @@ URLSearchParams::Delete(const nsAString&
 
 void
 URLSearchParams::DeleteAll()
 {
   NS_NOTREACHED("Unexpected call to URLSearchParams::DeleteAll");
 }
 
 void
-URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver)
+URLSearchParams::NotifyObserver()
 {
-  NS_NOTREACHED("Unexpected call to URLSearchParams::NotifyObservers");
+  NS_NOTREACHED("Unexpected call to URLSearchParams::NotifyObserver");
 }
 
+void
+URLSearchParams::Invalidate()
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::Invalidate");
+}
+
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mock_Link_h__