Backed out changeset 87433694df01 (bug 1082734) for Gaia unit test failures.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 23 Oct 2014 11:32:35 -0400
changeset 224208 874815b0d42b7f60c701f9928fa218cd2fcb4564
parent 224207 ed7562a016faedd0a911adf6dfc7b040d1de07f3
child 224209 0b52ad3f007aabdaf84aa3e8d8a280c4f0228a88
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-esr52@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1082734
milestone36.0a1
backs out87433694df019680550e698e2e9ddfbccba0e8ef
Backed out changeset 87433694df01 (bug 1082734) for Gaia unit test failures. CLOSED TREE
browser/components/loop/test/mochitest/loop_fxa.sjs
content/base/src/Link.cpp
content/base/src/Link.h
content/html/content/src/HTMLAnchorElement.cpp
content/html/content/src/HTMLAreaElement.cpp
content/html/content/src/HTMLLinkElement.cpp
content/svg/content/src/SVGAElement.cpp
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
dom/base/URL.cpp
dom/base/URL.h
dom/base/URLSearchParams.cpp
dom/base/URLSearchParams.h
dom/base/nsLocation.cpp
dom/base/nsLocation.h
dom/base/test/mochitest.ini
dom/base/test/test_location_searchParams.html
dom/base/test/test_urlSearchParams.html
dom/webidl/Location.webidl
dom/webidl/URLUtils.webidl
dom/workers/URL.cpp
dom/workers/URL.h
dom/workers/test/urlSearchParams_worker.js
mobile/android/chrome/content/WebcompatReporter.js
services/fxaccounts/FxAccountsOAuthClient.jsm
testing/web-platform/meta/url/interfaces.html.ini
toolkit/components/places/tests/cpp/mock_Link.h
--- a/browser/components/loop/test/mochitest/loop_fxa.sjs
+++ b/browser/components/loop/test/mochitest/loop_fxa.sjs
@@ -6,17 +6,17 @@
  */
 
 "use strict";
 
 const REQUIRED_PARAMS = ["client_id", "content_uri", "oauth_uri", "profile_uri", "state"];
 const HAWK_TOKEN_LENGTH = 64;
 
 Components.utils.import("resource://gre/modules/NetUtil.jsm");
-Components.utils.importGlobalProperties(["URL", "URLSearchParams"]);
+Components.utils.importGlobalProperties(["URL"]);
 
 /**
  * Entry point for HTTP requests.
  */
 function handleRequest(request, response) {
   // Convert the query string to a path with a placeholder base of example.com
   let url = new URL(request.queryString.replace(/%3F.*/,""), "http://www.example.com");
   dump("loop_fxa.sjs request for: " + url.pathname + "\n");
@@ -220,18 +220,17 @@ function delete_registration(request, re
     return;
   }
 
   // Do some query string munging due to the SJS file using a base with a trailing "?"
   // making the path become a query parameter. This is because we aren't actually
   // registering endpoints at the root of the hostname e.g. /registration.
   let url = new URL(request.queryString.replace(/%3F.*/,""), "http://www.example.com");
   let registration = JSON.parse(getSharedState("/registration"));
-  let searchParams = new URLSearchParams(url.search.substr(1));
-  if (registration.simplePushURL == searchParams.get("simplePushURL")) {
+  if (registration.simplePushURL == url.searchParams.get("simplePushURL")) {
     setSharedState("/registration", "");
   } else {
     response.setStatusLine(request.httpVersion, 400, "Bad Request");
   }
 }
 
 /**
  * GET /get_registration
--- a/content/base/src/Link.cpp
+++ b/content/base/src/Link.cpp
@@ -210,16 +210,23 @@ Link::SetPathname(const nsAString &aPath
 
   (void)url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
   SetHrefAttribute(uri);
 }
 
 void
 Link::SetSearch(const nsAString& aSearch, ErrorResult& aError)
 {
+  SetSearchInternal(aSearch);
+  UpdateURLSearchParams();
+}
+
+void
+Link::SetSearchInternal(const nsAString& aSearch)
+{
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (!url) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   (void)url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
@@ -466,16 +473,17 @@ 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();
 
   // 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
@@ -554,10 +562,89 @@ Link::SizeOfExcludingThis(mozilla::Mallo
 
   // The following members don't need to be measured:
   // - mElement, because it is a pointer-to-self used to avoid QIs
   // - mHistory, because it is non-owning
 
   return n;
 }
 
+URLSearchParams*
+Link::SearchParams()
+{
+  CreateSearchParamsIfNeeded();
+  return mSearchParams;
+}
+
+void
+Link::SetSearchParams(URLSearchParams& aSearchParams)
+{
+  if (mSearchParams) {
+    mSearchParams->RemoveObserver(this);
+  }
+
+  mSearchParams = &aSearchParams;
+  mSearchParams->AddObserver(this);
+
+  nsAutoString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+}
+
+void
+Link::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
+{
+  MOZ_ASSERT(mSearchParams);
+  MOZ_ASSERT(mSearchParams == aSearchParams);
+
+  nsString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+}
+
+void
+Link::UpdateURLSearchParams()
+{
+  if (!mSearchParams) {
+    return;
+  }
+
+  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);
+}
+
+void
+Link::CreateSearchParamsIfNeeded()
+{
+  if (!mSearchParams) {
+    mSearchParams = new URLSearchParams();
+    mSearchParams->AddObserver(this);
+    UpdateURLSearchParams();
+  }
+}
+
+void
+Link::Unlink()
+{
+  if (mSearchParams) {
+    mSearchParams->RemoveObserver(this);
+    mSearchParams = nullptr;
+  }
+}
+
+void
+Link::Traverse(nsCycleCollectionTraversalCallback &cb)
+{
+  Link* tmp = this;
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearchParams);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/content/base/src/Link.h
+++ b/content/base/src/Link.h
@@ -8,31 +8,32 @@
  * This is the base class for all link classes.
  */
 
 #ifndef mozilla_dom_Link_h__
 #define mozilla_dom_Link_h__
 
 #include "mozilla/IHistory.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/dom/URLSearchParams.h"
 #include "nsIContent.h" // for nsLinkState
 
 namespace mozilla {
 
 class EventStates;
 
 namespace dom {
 
 class Element;
 
 #define MOZILLA_DOM_LINK_IMPLEMENTATION_IID               \
 { 0xb25edee6, 0xdd35, 0x4f8b,                             \
   { 0xab, 0x90, 0x66, 0xd0, 0xbd, 0x3c, 0x22, 0xd5 } }
 
-class Link : public nsISupports
+class Link : public URLSearchParamsObserver
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_LINK_IMPLEMENTATION_IID)
 
   /**
    * aElement is the element pointer corresponding to this link.
    */
   explicit Link(Element* aElement);
@@ -58,26 +59,28 @@ public:
    */
   void SetProtocol(const nsAString &aProtocol, ErrorResult& aError);
   void SetUsername(const nsAString &aUsername, ErrorResult& aError);
   void SetPassword(const nsAString &aPassword, ErrorResult& aError);
   void SetHost(const nsAString &aHost, ErrorResult& aError);
   void SetHostname(const nsAString &aHostname, ErrorResult& aError);
   void SetPathname(const nsAString &aPathname, ErrorResult& aError);
   void SetSearch(const nsAString &aSearch, ErrorResult& aError);
+  void SetSearchParams(mozilla::dom::URLSearchParams& aSearchParams);
   void SetPort(const nsAString &aPort, ErrorResult& aError);
   void SetHash(const nsAString &aHash, ErrorResult& aError);
   void GetOrigin(nsAString &aOrigin, ErrorResult& aError);
   void GetProtocol(nsAString &_protocol, ErrorResult& aError);
   void GetUsername(nsAString &aUsername, ErrorResult& aError);
   void GetPassword(nsAString &aPassword, ErrorResult& aError);
   void GetHost(nsAString &_host, ErrorResult& aError);
   void GetHostname(nsAString &_hostname, ErrorResult& aError);
   void GetPathname(nsAString &_pathname, ErrorResult& aError);
   void GetSearch(nsAString &_search, ErrorResult& aError);
+  URLSearchParams* SearchParams();
   void GetPort(nsAString &_port, ErrorResult& aError);
   void GetHash(nsAString &_hash, ErrorResult& aError);
 
   /**
    * Invalidates any link caching, and resets the state to the default.
    *
    * @param aNotify
    *        true if ResetLinkState should notify the owning document about style
@@ -106,16 +109,19 @@ public:
    */
   virtual bool HasDeferredDNSPrefetchRequest() { return true; }
 
   virtual size_t
     SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   bool ElementHasHref() const;
 
+  // URLSearchParamsObserver
+  void URLSearchParamsUpdated(URLSearchParams* aSearchParams) MOZ_OVERRIDE;
+
 protected:
   virtual ~Link();
 
   /**
    * Return true if the link has associated URI.
    */
   bool HasURI() const
   {
@@ -124,39 +130,52 @@ 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.
    */
   void UnregisterFromHistory();
 
   already_AddRefed<nsIURI> GetURIToMutate();
   void SetHrefAttribute(nsIURI *aURI);
 
+  void CreateSearchParamsIfNeeded();
+
+  void SetSearchInternal(const nsAString& aSearch);
+
   mutable nsCOMPtr<nsIURI> mCachedURI;
 
   Element * const mElement;
 
   // Strong reference to History.  The link has to unregister before History
   // can disappear.
   nsCOMPtr<IHistory> mHistory;
 
   uint16_t mLinkState;
 
   bool mNeedsRegistration;
 
   bool mRegistered;
+
+protected:
+  nsRefPtr<URLSearchParams> mSearchParams;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(Link, MOZILLA_DOM_LINK_IMPLEMENTATION_IID)
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Link_h__
--- a/content/html/content/src/HTMLAnchorElement.cpp
+++ b/content/html/content/src/HTMLAnchorElement.cpp
@@ -51,21 +51,23 @@ NS_INTERFACE_TABLE_TAIL_INHERITING(nsGen
 
 NS_IMPL_ADDREF_INHERITED(HTMLAnchorElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLAnchorElement, Element)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLAnchorElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLAnchorElement,
                                                   nsGenericHTMLElement)
+  tmp->Link::Traverse(cb);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelList)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLAnchorElement,
                                                 nsGenericHTMLElement)
+  tmp->Link::Unlink();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelList)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ELEMENT_CLONE(HTMLAnchorElement)
 
 JSObject*
 HTMLAnchorElement::WrapNode(JSContext *aCx)
 {
--- a/content/html/content/src/HTMLAreaElement.cpp
+++ b/content/html/content/src/HTMLAreaElement.cpp
@@ -35,21 +35,23 @@ NS_INTERFACE_TABLE_TAIL_INHERITING(nsGen
 
 NS_IMPL_ADDREF_INHERITED(HTMLAreaElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLAreaElement, Element)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLAreaElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLAreaElement,
                                                   nsGenericHTMLElement)
+  tmp->Link::Traverse(cb);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelList)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLAreaElement,
                                                 nsGenericHTMLElement)
+  tmp->Link::Unlink();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelList)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ELEMENT_CLONE(HTMLAreaElement)
 
 
 NS_IMPL_STRING_ATTR(HTMLAreaElement, Alt, alt)
 NS_IMPL_STRING_ATTR(HTMLAreaElement, Coords, coords)
--- a/content/html/content/src/HTMLLinkElement.cpp
+++ b/content/html/content/src/HTMLLinkElement.cpp
@@ -45,23 +45,25 @@ HTMLLinkElement::~HTMLLinkElement()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLLinkElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLLinkElement,
                                                   nsGenericHTMLElement)
   tmp->nsStyleLinkElement::Traverse(cb);
+  tmp->Link::Traverse(cb);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelList)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImportLoader)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLLinkElement,
                                                 nsGenericHTMLElement)
   tmp->nsStyleLinkElement::Unlink();
+  tmp->Link::Unlink();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mImportLoader)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ADDREF_INHERITED(HTMLLinkElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLLinkElement, Element)
 
 
--- a/content/svg/content/src/SVGAElement.cpp
+++ b/content/svg/content/src/SVGAElement.cpp
@@ -43,20 +43,22 @@ NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION
                                nsIDOMSVGElement,
                                Link)
 NS_INTERFACE_TABLE_TAIL_INHERITING(SVGAElementBase)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(SVGAElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGAElement,
                                                   SVGAElementBase)
+  tmp->Link::Traverse(cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGAElement,
                                                 SVGAElementBase)
+  tmp->Link::Unlink();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ADDREF_INHERITED(SVGAElement, SVGAElementBase)
 NS_IMPL_RELEASE_INHERITED(SVGAElement, SVGAElementBase)
 
 //----------------------------------------------------------------------
 // Implementation
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -188,16 +188,17 @@
 #include "nsIStringBundle.h"
 #include "nsISupportsArray.h"
 #include "nsIURIFixup.h"
 #include "nsIURILoader.h"
 #include "nsIWebBrowserFind.h"
 #include "nsIWidget.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/URLSearchParams.h"
 
 #ifdef MOZ_TOOLKIT_SEARCH
 #include "nsIBrowserSearchService.h"
 #endif
 
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
@@ -1954,16 +1955,34 @@ nsDocShell::SetCurrentURI(nsIURI *aURI, 
     {
         // This is the root docshell
         isRoot = true;
     }
     if (mLSHE) {
         mLSHE->GetIsSubFrame(&isSubFrame);
     }
 
+    // nsDocShell owns a URLSearchParams that is used by
+    // window.location.searchParams to be in sync with the current location.
+    if (!mURLSearchParams) {
+      mURLSearchParams = new URLSearchParams();
+    }
+
+    nsAutoCString search;
+
+    nsCOMPtr<nsIURL> url(do_QueryInterface(mCurrentURI));
+    if (url) {
+      nsresult rv = url->GetQuery(search);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to get the query from a nsIURL.");
+      }
+    }
+
+    mURLSearchParams->ParseInput(search, nullptr);
+
     if (!isSubFrame && !isRoot) {
       /* 
        * We don't want to send OnLocationChange notifications when
        * a subframe is being loaded for the first time, while
        * visiting a frameset page
        */
       return false; 
     }
@@ -5607,16 +5626,21 @@ nsDocShell::Destroy()
         mContentViewer = nullptr;
     }
 
     nsDocLoader::Destroy();
     
     mParentWidget = nullptr;
     mCurrentURI = nullptr;
 
+    if (mURLSearchParams) {
+      mURLSearchParams->RemoveObservers();
+      mURLSearchParams = nullptr;
+    }
+
     if (mScriptGlobal) {
         mScriptGlobal->DetachFromDocShell();
         mScriptGlobal = nullptr;
     }
 
     if (mSessionHistory) {
         // We want to destroy these content viewers now rather than
         // letting their destruction wait for the session history
@@ -13525,16 +13549,22 @@ nsDocShell::SetOpenedRemote(nsITabParent
 
 nsITabParent*
 nsDocShell::GetOpenedRemote()
 {
   nsCOMPtr<nsITabParent> openedRemote(do_QueryReferent(mOpenedRemote));
   return openedRemote;
 }
 
+URLSearchParams*
+nsDocShell::GetURLSearchParams()
+{
+  return mURLSearchParams;
+}
+
 void
 nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString &aProvider,
                                             const nsString &aKeyword) {
 
   if (aProvider.IsEmpty()) {
     return;
   }
 
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -52,16 +52,17 @@
 #include "nsCRT.h"
 #include "prtime.h"
 #include "nsRect.h"
 #include "Units.h"
 
 namespace mozilla {
 namespace dom {
 class EventTarget;
+class URLSearchParams;
 }
 }
 
 class nsDocShell;
 class nsDOMNavigationTiming;
 class nsGlobalWindow;
 class nsIController;
 class nsIScrollableFrame;
@@ -791,16 +792,19 @@ protected:
 
     // Set in LoadErrorPage from the method argument and used later
     // in CreateContentViewer. We have to delay an shistory entry creation
     // for which these objects are needed.
     nsCOMPtr<nsIURI>           mFailedURI;
     nsCOMPtr<nsIChannel>       mFailedChannel;
     uint32_t                   mFailedLoadType;
 
+    // window.location.searchParams is updated in sync with this object.
+    nsRefPtr<mozilla::dom::URLSearchParams> mURLSearchParams;
+
     // Set in DoURILoad when either the LOAD_RELOAD_ALLOW_MIXED_CONTENT flag or
     // the LOAD_NORMAL_ALLOW_MIXED_CONTENT flag is set.
     // Checked in nsMixedContentBlocker, to see if the channels match.
     nsCOMPtr<nsIChannel>       mMixedContentChannel;
 
     // WEAK REFERENCES BELOW HERE.
     // Note these are intentionally not addrefd.  Doing so will create a cycle.
     // For that reasons don't use nsCOMPtr.
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -7,24 +7,31 @@
 #include "domstubs.idl"
 #include "nsIDocShellTreeItem.idl"
 
 %{ C++
 #include "js/TypeDecls.h"
 class nsPresContext;
 class nsIPresShell;
 
+namespace mozilla {
+namespace dom {
+class URLSearchParams;
+}
+}
+
 %}
 
 /**
  * The nsIDocShell interface.
  */
 
 [ptr] native nsPresContext(nsPresContext);
 [ptr] native nsIPresShell(nsIPresShell);
+[ptr] native URLSearchParams(mozilla::dom::URLSearchParams);
 
 interface nsIURI;
 interface nsIChannel;
 interface nsIContentViewer;
 interface nsIURIContentListener;
 interface nsIDOMEventTarget;
 interface nsIDocShellLoadInfo;
 interface nsIEditor;
@@ -42,17 +49,17 @@ interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
  
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(cecf29f4-3eee-41ff-a76b-b36c73e271b1)]
+[scriptable, builtinclass, uuid(da8f78f1-8f20-4d6d-be56-fe53e177b630)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -1011,15 +1018,18 @@ interface nsIDocShell : nsIDocShellTreeI
 
   /**
    * See the documentation for setOpener and getOpener about why we
    * don't use attribute here instead.
    */
   [noscript,notxpcom,nostdcall] void setOpenedRemote(in nsITabParent aOpenedRemote);
   [noscript,notxpcom,nostdcall] nsITabParent getOpenedRemote();
 
+  // URLSearchParams for the window.location is owned by the docShell.
+  [noscript,notxpcom] URLSearchParams getURLSearchParams();
+
   /**
    * This attribute determines whether a document which is not about:blank has
    * already be loaded by this docShell.
    */
   [infallible] readonly attribute boolean hasLoadedNonBlankURI;
 
 };
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -19,19 +19,24 @@
 #include "nsIURL.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(URL)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(URL)
+  if (tmp->mSearchParams) {
+    tmp->mSearchParams->RemoveObserver(tmp);
+    NS_IMPL_CYCLE_COLLECTION_UNLINK(mSearchParams)
+  }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(URL)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearchParams)
 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
@@ -223,16 +228,17 @@ 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;
   }
 
   mURI = uri;
+  UpdateURLSearchParams();
 }
 
 void
 URL::GetOrigin(nsString& aOrigin, ErrorResult& aRv) const
 {
   nsCOMPtr<nsIURIWithPrincipal> uriWithPrincipal = do_QueryInterface(mURI);
   if (uriWithPrincipal) {
     nsCOMPtr<nsIPrincipal> principal;
@@ -338,16 +344,46 @@ URL::GetHost(nsString& aHost, ErrorResul
 
 void
 URL::SetHost(const nsAString& aHost, ErrorResult& aRv)
 {
   mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
 }
 
 void
+URL::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
+{
+  MOZ_ASSERT(mSearchParams);
+  MOZ_ASSERT(mSearchParams == aSearchParams);
+
+  nsAutoString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+}
+
+void
+URL::UpdateURLSearchParams()
+{
+  if (!mSearchParams) {
+    return;
+  }
+
+  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);
+}
+
+void
 URL::GetHostname(nsString& aHostname, ErrorResult& aRv) const
 {
   aHostname.Truncate();
   nsContentUtils::GetHostOrIPv6WithBrackets(mURI, aHostname);
 }
 
 void
 URL::SetHostname(const nsAString& aHostname, ErrorResult& aRv)
@@ -437,25 +473,55 @@ URL::GetSearch(nsString& aSearch, ErrorR
   if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
     CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
   }
 }
 
 void
 URL::SetSearch(const nsAString& aSearch, ErrorResult& aRv)
 {
+  SetSearchInternal(aSearch);
+  UpdateURLSearchParams();
+}
+
+void
+URL::SetSearchInternal(const nsAString& aSearch)
+{
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (!url) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
 }
 
+URLSearchParams*
+URL::SearchParams()
+{
+  CreateSearchParamsIfNeeded();
+  return mSearchParams;
+}
+
+void
+URL::SetSearchParams(URLSearchParams& aSearchParams)
+{
+  if (mSearchParams) {
+    mSearchParams->RemoveObserver(this);
+  }
+
+  // the observer will be cleared using the cycle collector.
+  mSearchParams = &aSearchParams;
+  mSearchParams->AddObserver(this);
+
+  nsAutoString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+}
+
 void
 URL::GetHash(nsString& aHash, ErrorResult& aRv) const
 {
   aHash.Truncate();
 
   nsAutoCString ref;
   nsresult rv = mURI->GetRef(ref);
   if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
@@ -474,10 +540,20 @@ URL::SetHash(const nsAString& aHash, Err
 bool IsChromeURI(nsIURI* aURI)
 {
   bool isChrome = false;
   if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)))
       return isChrome;
   return false;
 }
 
+void
+URL::CreateSearchParamsIfNeeded()
+{
+  if (!mSearchParams) {
+    mSearchParams = new URLSearchParams();
+    mSearchParams->AddObserver(this);
+    UpdateURLSearchParams();
+  }
+}
+
 }
 }
--- a/dom/base/URL.h
+++ b/dom/base/URL.h
@@ -1,16 +1,17 @@
 /* -*- 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 "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/URLSearchParams.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 
 class nsIDOMBlob;
 class nsIPrincipal;
 class nsISupports;
 class nsIURI;
@@ -26,17 +27,17 @@ class File;
 class MediaSource;
 class GlobalObject;
 struct objectURLOptions;
 
 namespace workers {
 class URLProxy;
 }
 
-class URL MOZ_FINAL : public nsISupports
+class URL MOZ_FINAL : public URLSearchParamsObserver
 {
   ~URL() {}
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(URL)
 
   explicit URL(nsIURI* aURI);
@@ -103,39 +104,53 @@ public:
   void GetPathname(nsString& aPathname, ErrorResult& aRv) const;
 
   void SetPathname(const nsAString& aPathname, ErrorResult& aRv);
 
   void GetSearch(nsString& aRetval, ErrorResult& aRv) const;
 
   void SetSearch(const nsAString& aArg, ErrorResult& aRv);
 
+  URLSearchParams* SearchParams();
+
+  void SetSearchParams(URLSearchParams& aSearchParams);
+
   void GetHash(nsString& aRetval, ErrorResult& aRv) const;
 
   void SetHash(const nsAString& aArg, ErrorResult& aRv);
 
   void Stringify(nsString& aRetval, ErrorResult& aRv) const
   {
     GetHref(aRetval, aRv);
   }
 
+  // URLSearchParamsObserver
+  void URLSearchParamsUpdated(URLSearchParams* aSearchParams) 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;
+  nsRefPtr<URLSearchParams> mSearchParams;
 
   friend class mozilla::dom::workers::URLProxy;
 };
 
 bool IsChromeURI(nsIURI* aURI);
 
 }
 }
--- a/dom/base/URLSearchParams.cpp
+++ b/dom/base/URLSearchParams.cpp
@@ -6,17 +6,17 @@
 #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_0(URLSearchParams)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLSearchParams, mObservers)
 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
 
@@ -36,32 +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));
+  sp->ParseInput(NS_ConvertUTF16toUTF8(aInit), nullptr);
   return sp.forget();
 }
 
 /* static */ already_AddRefed<URLSearchParams>
 URLSearchParams::Constructor(const GlobalObject& aGlobal,
                              URLSearchParams& aInit,
                              ErrorResult& aRv)
 {
   nsRefPtr<URLSearchParams> sp = new URLSearchParams();
   sp->mSearchParams = aInit.mSearchParams;
   return sp.forget();
 }
 
 void
-URLSearchParams::ParseInput(const nsACString& aInput)
+URLSearchParams::ParseInput(const nsACString& aInput,
+                            URLSearchParamsObserver* aObserver)
 {
   // 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);
@@ -99,18 +100,20 @@ URLSearchParams::ParseInput(const nsACSt
     }
 
     nsAutoString decodedName;
     DecodeString(name, decodedName);
 
     nsAutoString decodedValue;
     DecodeString(value, decodedValue);
 
-    Append(decodedName, decodedValue);
+    AppendInternal(decodedName, decodedValue);
   }
+
+  NotifyObservers(aObserver);
 }
 
 void
 URLSearchParams::DecodeString(const nsACString& aInput, nsAString& aOutput)
 {
   nsACString::const_iterator start, end;
   aInput.BeginReading(start);
   aInput.EndReading(end);
@@ -200,16 +203,37 @@ URLSearchParams::ConvertString(const nsA
   }
 
   if (newOutputLength < outputLength) {
     aOutput.Truncate(newOutputLength);
   }
 }
 
 void
+URLSearchParams::AddObserver(URLSearchParamsObserver* aObserver)
+{
+  MOZ_ASSERT(aObserver);
+  MOZ_ASSERT(!mObservers.Contains(aObserver));
+  mObservers.AppendElement(aObserver);
+}
+
+void
+URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver)
+{
+  MOZ_ASSERT(aObserver);
+  mObservers.RemoveElement(aObserver);
+}
+
+void
+URLSearchParams::RemoveObservers()
+{
+  mObservers.Clear();
+}
+
+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;
@@ -249,21 +273,30 @@ URLSearchParams::Set(const nsAString& aN
   }
 
   if (!param) {
     param = mSearchParams.AppendElement();
     param->mKey = aName;
   }
 
   param->mValue = aValue;
+
+  NotifyObservers(nullptr);
 }
 
 void
 URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
 {
+  AppendInternal(aName, aValue);
+  NotifyObservers(nullptr);
+}
+
+void
+URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue)
+{
   Param* param = mSearchParams.AppendElement();
   param->mKey = aName;
   param->mValue = aValue;
 }
 
 bool
 URLSearchParams::Has(const nsAString& aName)
 {
@@ -274,23 +307,29 @@ URLSearchParams::Has(const nsAString& aN
   }
 
   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;
     }
   }
+
+  if (found) {
+    NotifyObservers(nullptr);
+  }
 }
 
 void
 URLSearchParams::DeleteAll()
 {
   mSearchParams.Clear();
 }
 
@@ -334,10 +373,20 @@ URLSearchParams::Serialize(nsAString& aV
     }
 
     SerializeString(NS_ConvertUTF16toUTF8(mSearchParams[i].mKey), aValue);
     aValue.Append('=');
     SerializeString(NS_ConvertUTF16toUTF8(mSearchParams[i].mValue), aValue);
   }
 }
 
+void
+URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver)
+{
+  for (uint32_t i = 0; i < mObservers.Length(); ++i) {
+    if (mObservers[i] != aExceptObserver) {
+      mObservers[i]->URLSearchParamsUpdated(this);
+    }
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/URLSearchParams.h
+++ b/dom/base/URLSearchParams.h
@@ -13,16 +13,24 @@
 #include "nsISupports.h"
 #include "nsIUnicodeDecoder.h"
 
 namespace mozilla {
 namespace dom {
 
 class URLSearchParams;
 
+class URLSearchParamsObserver : public nsISupports
+{
+public:
+  virtual ~URLSearchParamsObserver() {}
+
+  virtual void URLSearchParamsUpdated(URLSearchParams* aFromThis) = 0;
+};
+
 class URLSearchParams MOZ_FINAL : public nsISupports,
                                   public nsWrapperCache
 {
   ~URLSearchParams();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(URLSearchParams)
@@ -41,17 +49,22 @@ 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);
+  void ParseInput(const nsACString& aInput,
+                  URLSearchParamsObserver* aObserver);
+
+  void AddObserver(URLSearchParamsObserver* aObserver);
+  void RemoveObserver(URLSearchParamsObserver* aObserver);
+  void RemoveObservers();
 
   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);
@@ -63,28 +76,33 @@ public:
   void Delete(const nsAString& aName);
 
   void Stringify(nsString& aRetval) const
   {
     Serialize(aRetval);
   }
 
 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 NotifyObservers(URLSearchParamsObserver* aExceptObserver);
+
   struct Param
   {
     nsString mKey;
     nsString mValue;
   };
 
   nsTArray<Param> mSearchParams;
 
+  nsTArray<nsRefPtr<URLSearchParamsObserver>> mObservers;
   nsCOMPtr<nsIUnicodeDecoder> mDecoder;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_URLSearchParams_h */
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -56,33 +56,37 @@ nsLocation::nsLocation(nsPIDOMWindow* aW
   MOZ_ASSERT(aDocShell);
   MOZ_ASSERT(mInnerWindow->IsInnerWindow());
 
   mDocShell = do_GetWeakReference(aDocShell);
 }
 
 nsLocation::~nsLocation()
 {
+  RemoveURLSearchParams();
 }
 
 // QueryInterface implementation for nsLocation
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsLocation)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIDOMLocation)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMLocation)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsLocation)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsLocation)
+  tmp->RemoveURLSearchParams();
+
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mInnerWindow);
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsLocation)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearchParams)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInnerWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsLocation)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsLocation)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsLocation)
@@ -1037,8 +1041,124 @@ nsLocation::CallerSubsumes()
   return subsumes;
 }
 
 JSObject*
 nsLocation::WrapObject(JSContext* aCx)
 {
   return LocationBinding::Wrap(aCx, this);
 }
+
+URLSearchParams*
+nsLocation::GetDocShellSearchParams()
+{
+  nsCOMPtr<nsIDocShell> docShell = GetDocShell();
+  if (!docShell) {
+    return nullptr;
+  }
+
+  return docShell->GetURLSearchParams();
+}
+
+URLSearchParams*
+nsLocation::SearchParams()
+{
+  if (!mSearchParams) {
+    // We must register this object to the URLSearchParams of the docshell in
+    // order to receive updates.
+    nsRefPtr<URLSearchParams> searchParams = GetDocShellSearchParams();
+    if (searchParams) {
+      searchParams->AddObserver(this);
+    }
+
+    mSearchParams = new URLSearchParams();
+    mSearchParams->AddObserver(this);
+    UpdateURLSearchParams();
+  }
+
+  return mSearchParams;
+}
+
+void
+nsLocation::SetSearchParams(URLSearchParams& aSearchParams)
+{
+  if (mSearchParams) {
+    mSearchParams->RemoveObserver(this);
+  }
+
+  // the observer will be cleared using the cycle collector.
+  mSearchParams = &aSearchParams;
+  mSearchParams->AddObserver(this);
+
+  nsAutoString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+
+  // We don't need to inform the docShell about this new SearchParams because
+  // setting the new value the docShell will refresh its value automatically.
+}
+
+void
+nsLocation::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
+{
+  MOZ_ASSERT(mSearchParams);
+
+  // This change comes from content.
+  if (aSearchParams == mSearchParams) {
+    nsAutoString search;
+    mSearchParams->Serialize(search);
+    SetSearchInternal(search);
+    return;
+  }
+
+  // This change comes from the docShell.
+#ifdef DEBUG
+  {
+    nsRefPtr<URLSearchParams> searchParams = GetDocShellSearchParams();
+    MOZ_ASSERT(searchParams);
+    MOZ_ASSERT(aSearchParams == searchParams);
+  }
+#endif
+
+  nsAutoString search;
+  aSearchParams->Serialize(search);
+  mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(search), this);
+}
+
+void
+nsLocation::UpdateURLSearchParams()
+{
+  if (!mSearchParams) {
+    return;
+  }
+
+  nsAutoCString search;
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = GetURI(getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!uri)) {
+    return;
+  }
+
+  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);
+}
+
+void
+nsLocation::RemoveURLSearchParams()
+{
+  if (mSearchParams) {
+    mSearchParams->RemoveObserver(this);
+    mSearchParams = nullptr;
+
+    nsRefPtr<URLSearchParams> docShellSearchParams = GetDocShellSearchParams();
+    if (docShellSearchParams) {
+      docShellSearchParams->RemoveObserver(this);
+    }
+  }
+}
--- a/dom/base/nsLocation.h
+++ b/dom/base/nsLocation.h
@@ -9,28 +9,30 @@
 
 #include "nsIDOMLocation.h"
 #include "nsString.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "js/TypeDecls.h"
 #include "mozilla/ErrorResult.h"
+#include "mozilla/dom/URLSearchParams.h"
 #include "nsPIDOMWindow.h"
 
 class nsIURI;
 class nsIDocShell;
 class nsIDocShellLoadInfo;
 
 //*****************************************************************************
 // nsLocation: Script "location" object
 //*****************************************************************************
 
 class nsLocation MOZ_FINAL : public nsIDOMLocation
                            , public nsWrapperCache
+                           , public mozilla::dom::URLSearchParamsObserver
 {
   typedef mozilla::ErrorResult ErrorResult;
 
 public:
   nsLocation(nsPIDOMWindow* aWindow, nsIDocShell *aDocShell);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsLocation,
@@ -115,16 +117,20 @@ public:
   {
     aError = GetSearch(aSeach);
   }
   void SetSearch(const nsAString& aSeach, ErrorResult& aError)
   {
     aError = SetSearch(aSeach);
   }
 
+  mozilla::dom::URLSearchParams* SearchParams();
+
+  void SetSearchParams(mozilla::dom::URLSearchParams& aSearchParams);
+
   void GetHash(nsAString& aHash, ErrorResult& aError)
   {
     aError = GetHash(aHash);
   }
   void SetHash(const nsAString& aHash, ErrorResult& aError)
   {
     aError = SetHash(aHash);
   }
@@ -133,20 +139,27 @@ public:
     GetHref(aRetval, aError);
   }
   nsPIDOMWindow* GetParentObject() const
   {
     return mInnerWindow;
   }
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
+  // URLSearchParamsObserver
+  void URLSearchParamsUpdated(mozilla::dom::URLSearchParams* aSearchParams) MOZ_OVERRIDE;
+
 protected:
   virtual ~nsLocation();
 
   nsresult SetSearchInternal(const nsAString& aSearch);
+  void UpdateURLSearchParams();
+  void RemoveURLSearchParams();
+
+  mozilla::dom::URLSearchParams* GetDocShellSearchParams();
 
   // In the case of jar: uris, we sometimes want the place the jar was
   // fetched from as the URI instead of the jar: uri itself.  Pass in
   // true for aGetInnermostURI when that's the case.
   nsresult GetURI(nsIURI** aURL, bool aGetInnermostURI = false);
   nsresult GetWritableURI(nsIURI** aURL);
   nsresult SetURI(nsIURI* aURL, bool aReplace = false);
   nsresult SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
@@ -155,13 +168,14 @@ protected:
                               bool aReplace);
 
   nsresult GetSourceBaseURL(JSContext* cx, nsIURI** sourceURL);
   nsresult CheckURL(nsIURI *url, nsIDocShellLoadInfo** aLoadInfo);
   bool CallerSubsumes();
 
   nsString mCachedHash;
   nsCOMPtr<nsPIDOMWindow> mInnerWindow;
+  nsRefPtr<mozilla::dom::URLSearchParams> mSearchParams;
   nsWeakPtr mDocShell;
 };
 
 #endif // nsLocation_h__
 
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -45,16 +45,17 @@ skip-if = buildapp == 'mulet' || buildap
 [test_gsp-standards.html]
 [test_getFeature_with_perm.html]
 [test_getFeature_without_perm.html]
 [test_hasFeature.html]
 [test_history_document_open.html]
 [test_history_state_null.html]
 [test_Image_constructor.html]
 [test_innersize_scrollport.html]
+[test_location_searchParams.html]
 [test_messageChannel.html]
 [test_messageChannel_cloning.html]
 [test_messageChannel_pingpong.html]
 [test_messageChannel_post.html]
 [test_messageChannel_pref.html]
 [test_messageChannel_start.html]
 [test_messagemanager_targetchain.html]
 [test_messageChannel_transferable.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_location_searchParams.html
@@ -0,0 +1,89 @@
+
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1037715
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1037715</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=1037715">Mozilla Bug 1037715</a>
+  <iframe id="a"></iframe>
+  <script type="application/javascript">
+
+var l;
+
+var iframe = document.getElementById('a');
+function onload0() {
+  iframe.removeEventListener('load', onload0);
+
+  l = iframe.contentWindow.location;
+  is(l.searchParams.get('a'), 'test0', 'l.searchParams value is ok');
+
+  info('changing location from JS...');
+  iframe.addEventListener('load', onload1);
+  iframe.contentWindow.location.href = 'file_empty.html?a=test1';
+}
+
+function onload1() {
+  iframe.removeEventListener('load', onload1);
+
+  var ll = iframe.contentWindow.location;
+  is(ll.searchParams.get('a'), 'test1', 'location.searchParams value is ok');
+  is(l.searchParams.get('a'), 'test1', 'l.searchParams value is ok');
+  isnot(ll.searchParams, l.searchParams, '2 different objects.');
+
+  info('changing location using l.searchParams...');
+  iframe.addEventListener('load', onload2);
+  l.searchParams.set('a', 'test2');
+}
+
+function onload2() {
+  iframe.removeEventListener('load', onload2);
+
+  var ll = iframe.contentWindow.location;
+  is(ll.searchParams.get('a'), 'test2', 'location.searchParams value is ok');
+  is(l.searchParams.get('a'), 'test2', 'l.searchParams value is ok');
+  isnot(ll.searchParams, l.searchParams, '2 different objects.');
+
+  info('changing iframe.src...');
+  iframe.addEventListener('load', onload3);
+  l.search = 'a=test3';
+}
+
+function onload3() {
+  iframe.removeEventListener('load', onload3);
+
+  var ll = iframe.contentWindow.location;
+  is(ll.searchParams.get('a'), 'test3', 'location.searchParams value is ok');
+  is(l.searchParams.get('a'), 'test3', 'l.searchParams value is ok');
+  isnot(ll.searchParams, l.searchParams, '2 different objects.');
+
+  info('changing iframe.src...');
+  iframe.addEventListener('load', onload4);
+  iframe.src = 'file_empty.html?a=test4';
+}
+
+function onload4() {
+  iframe.removeEventListener('load', onload4);
+
+  var ll = iframe.contentWindow.location;
+  is(ll.searchParams.get('a'), 'test4', 'location.searchParams value is ok');
+  is(l.searchParams.get('a'), 'test4', 'l.searchParams value is ok');
+  isnot(ll.searchParams, l.searchParams, '2 different objects.');
+
+  SimpleTest.finish();
+}
+
+iframe.addEventListener('load', onload0);
+iframe.src = "file_empty.html?a=test0";
+SimpleTest.waitForExplicitFinish();
+
+  </script>
+
+</body>
+</html>
--- a/dom/base/test/test_urlSearchParams.html
+++ b/dom/base/test/test_urlSearchParams.html
@@ -108,31 +108,151 @@ https://bugzilla.mozilla.org/show_bug.cg
           is(all[k], checks[i].data[key][k], "value matches");
         }
       }
     }
 
     runTest();
   }
 
+  function testURL() {
+    var url = new URL('http://www.example.net?a=b&c=d');
+    ok(url.searchParams, "URL searchParams exists!");
+    ok(url.searchParams.has('a'), "URL.searchParams.has('a')");
+    is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')");
+    ok(url.searchParams.has('c'), "URL.searchParams.has('c')");
+    is(url.searchParams.get('c'), 'd', "URL.searchParams.get('c')");
+
+    url.searchParams.set('e', 'f');
+    ok(url.href.indexOf('e=f') != 1, 'URL right');
+
+    var u = new URLSearchParams();
+    u.append('foo', 'bar');
+    url.searchParams = u;
+    is(url.searchParams, u, "URL.searchParams is the same object");
+    is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
+    is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
+
+    try {
+      url.searchParams = null;
+      ok(false, "URLSearchParams is not nullable");
+    } catch(e) {
+      ok(true, "URLSearchParams is not nullable");
+    }
+
+    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");
+    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();
+  }
+
+  function testElement(e) {
+    ok(e, 'element exists');
+    ok(e.searchParams, "e.searchParams exists!");
+    ok(e.searchParams.has('a'), "e.searchParams.has('a')");
+    is(e.searchParams.get('a'), 'b', "e.searchParams.get('a')");
+    ok(e.searchParams.has('c'), "e.searchParams.has('c')");
+    is(e.searchParams.get('c'), 'd', "e.searchParams.get('c')");
+
+    e.searchParams.set('e', 'f');
+    ok(e.href.indexOf('e=f') != 1, 'e is right');
+
+    var u = new URLSearchParams();
+    u.append('foo', 'bar');
+    e.searchParams = u;
+    is(e.searchParams, u, "e.searchParams is the same object");
+    is(e.searchParams.get('foo'), 'bar', "e.searchParams.get('foo')");
+    is(e.href, 'http://www.example.net/?foo=bar', 'e is right');
+
+    try {
+      e.searchParams = null;
+      ok(false, "URLSearchParams is not nullable");
+    } catch(e) {
+      ok(true, "URLSearchParams is not nullable");
+    }
+
+    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");
+    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')");
+
+    runTest();
+  }
+
   function testEncoding() {
     var encoding = [ [ '1', '1' ],
                      [ 'a b', 'a+b' ],
                      [ '<>', '%3C%3E' ],
                      [ '\u0541', '%D5%81'] ];
 
     for (var i = 0; i < encoding.length; ++i) {
       var a = new URLSearchParams();
       a.set('a', encoding[i][0]);
-      is(a + "", "a=" + encoding[i][1]);
+
+      var url = new URL('http://www.example.net');
+      url.searchParams = a;
+      is(url.href, 'http://www.example.net/?a=' + encoding[i][1]);
+
+      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();
+  }
+
   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');
@@ -153,65 +273,73 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(a.toString(), "b=3&c=4&c=5", "Order is correct");
 
     runTest();
   }
 
   function testGetNULL() {
       
     var u = new URLSearchParams();
-    is(typeof u.get(''), "object", "typeof searchParams.get('')");
-    is(u.get(''), null, "searchParams.get('') should be null");
+    is(typeof u.get(''), "object", "typeof URL.searchParams.get('')");
+    is(u.get(''), null, "URL.searchParams.get('') should be null");
+
+    var url = new URL('http://www.example.net?a=b');
+    is(url.searchParams.get('b'), null, "URL.searchParams.get('b') should be null");
+    is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')");
 
     runTest();
   }
 
   function testSet() {
     var u = new URLSearchParams();
     u.set('a','b');
     u.set('e','c');
     u.set('i','d');
     u.set('o','f');
     u.set('u','g');
 
-    is(u.get('a'), 'b', "searchParams.get('a') should return b");
+    is(u.get('a'), 'b', "URL.searchParams.get('a') should return b");
     is(u.getAll('a').length, 1, "URLSearchParams.getAll('a').length should be 1");
 
     u.set('a','h1');
     u.set('a','h2');
     u.set('a','h3');
     u.set('a','h4');
-    is(u.get('a'), 'h4', "searchParams.get('a') should return h4");
+    is(u.get('a'), 'h4', "URL.searchParams.get('a') should return h4");
     is(u.getAll('a').length, 1, "URLSearchParams.getAll('a').length should be 1");
 
-    is(u.get('e'), 'c', "searchParams.get('e') should return c");
-    is(u.get('i'), 'd', "searchParams.get('i') should return d");
-    is(u.get('o'), 'f', "searchParams.get('o') should return f");
-    is(u.get('u'), 'g', "searchParams.get('u') should return g");
+    is(u.get('e'), 'c', "URL.searchParams.get('e') should return c");
+    is(u.get('i'), 'd', "URL.searchParams.get('i') should return d");
+    is(u.get('o'), 'f', "URL.searchParams.get('o') should return f");
+    is(u.get('u'), 'g', "URL.searchParams.get('u') should return g");
 
     is(u.getAll('e').length, 1, "URLSearchParams.getAll('e').length should be 1");
     is(u.getAll('i').length, 1, "URLSearchParams.getAll('i').length should be 1");
     is(u.getAll('o').length, 1, "URLSearchParams.getAll('o').length should be 1");
     is(u.getAll('u').length, 1, "URLSearchParams.getAll('u').length should be 1");
 
     u = new URLSearchParams("name1=value1&name1=value2&name1=value3");
-    is(u.get('name1'), 'value1', "searchParams.get('name1') should return value1");
+    is(u.get('name1'), 'value1', "URL.searchParams.get('name1') should return value1");
     is(u.getAll('name1').length, 3, "URLSearchParams.getAll('name1').length should be 3");
     u.set('name1','firstPair');
-    is(u.get('name1'), 'firstPair', "searchParams.get('name1') should return firstPair");
+    is(u.get('name1'), 'firstPair', "URL.searchParams.get('name1') should return firstPair");
     is(u.getAll('name1').length, 1, "URLSearchParams.getAll('name1').length should be 1");
 
     runTest();
   }
 
   var tests = [
     testSimpleURLSearchParams,
     testCopyURLSearchParams,
     testParserURLSearchParams,
+    testURL,
+    function() { testElement(document.getElementById('anchor')) },
+    function() { testElement(document.getElementById('area')) },
     testEncoding,
+    testMultiURL,
     testOrdering,
     testDelete,
     testGetNULL,
     testSet
   ];
 
   function runTest() {
     if (!tests.length) {
--- a/dom/webidl/Location.webidl
+++ b/dom/webidl/Location.webidl
@@ -16,10 +16,10 @@ interface Location {
   [Throws]
   void assign(DOMString url);
   [Throws, CrossOriginCallable]
   void replace(DOMString url);
   // XXXbz there is no forceget argument in the spec!  See bug 1037721.
   [Throws]
   void reload(optional boolean forceget = false);
 };
-
+// No support for .searchParams on Location yet.  See bug 1037715.
 Location implements URLUtils;
--- a/dom/webidl/URLUtils.webidl
+++ b/dom/webidl/URLUtils.webidl
@@ -35,17 +35,17 @@ interface URLUtils {
            attribute ScalarValueString hostname;
   [Throws]
            attribute ScalarValueString port;
   [Throws]
            attribute ScalarValueString pathname;
   [Throws]
            attribute ScalarValueString search;
 
-   // Bug 1082734 - attribute URLSearchParams searchParams;
+           attribute URLSearchParams searchParams;
 
   [Throws]
            attribute ScalarValueString hash;
 
   // Bug 824857 should remove this.
   [Throws]
   stringifier;
 };
--- a/dom/workers/URL.cpp
+++ b/dom/workers/URL.cpp
@@ -7,16 +7,17 @@
 
 #include "nsIDocument.h"
 #include "nsIIOService.h"
 #include "nsPIDOMWindow.h"
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/URL.h"
 #include "mozilla/dom/URLBinding.h"
+#include "mozilla/dom/URLSearchParams.h"
 #include "nsGlobalWindow.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsNetCID.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
@@ -454,17 +455,17 @@ public:
 
 private:
   const nsString mValue;
   SetterType mType;
   nsRefPtr<URLProxy> mURLProxy;
   mozilla::ErrorResult& mRv;
 };
 
-NS_IMPL_CYCLE_COLLECTION_0(URL)
+NS_IMPL_CYCLE_COLLECTION(URL, mSearchParams)
 
 // The reason for using worker::URL is to have different refcnt logging than
 // for main thread URL.
 NS_IMPL_CYCLE_COLLECTING_ADDREF(workers::URL)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(workers::URL)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
@@ -565,16 +566,18 @@ 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();
 }
 
 void
 URL::GetOrigin(nsString& aOrigin, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterOrigin, aOrigin,
                        mURLProxy);
@@ -769,26 +772,55 @@ URL::GetSearch(nsString& aSearch, ErrorR
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
 URL::SetSearch(const nsAString& aSearch, ErrorResult& aRv)
 {
+  SetSearchInternal(aSearch);
+  UpdateURLSearchParams();
+}
+
+void
+URL::SetSearchInternal(const nsAString& aSearch)
+{
   ErrorResult rv;
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterSearch,
                        aSearch, mURLProxy, rv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
+mozilla::dom::URLSearchParams*
+URL::SearchParams()
+{
+  CreateSearchParamsIfNeeded();
+  return mSearchParams;
+}
+
+void
+URL::SetSearchParams(URLSearchParams& aSearchParams)
+{
+  if (mSearchParams) {
+    mSearchParams->RemoveObserver(this);
+  }
+
+  mSearchParams = &aSearchParams;
+  mSearchParams->AddObserver(this);
+
+  nsString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+}
+
 void
 URL::GetHash(nsString& aHash, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHash, aHash,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
@@ -849,9 +881,41 @@ URL::RevokeObjectURL(const GlobalObject&
   nsRefPtr<RevokeURLRunnable> runnable =
     new RevokeURLRunnable(workerPrivate, aUrl);
 
   if (!runnable->Dispatch(cx)) {
     JS_ReportPendingException(cx);
   }
 }
 
+void
+URL::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
+{
+  MOZ_ASSERT(mSearchParams);
+  MOZ_ASSERT(mSearchParams == aSearchParams);
+
+  nsString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+}
+
+void
+URL::UpdateURLSearchParams()
+{
+  if (mSearchParams) {
+    nsString search;
+    ErrorResult rv;
+    GetSearch(search, rv);
+    mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1)), this);
+  }
+}
+
+void
+URL::CreateSearchParamsIfNeeded()
+{
+  if (!mSearchParams) {
+    mSearchParams = new URLSearchParams();
+    mSearchParams->AddObserver(this);
+    UpdateURLSearchParams();
+  }
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/URL.h
+++ b/dom/workers/URL.h
@@ -6,33 +6,35 @@
 
 #ifndef mozilla_dom_workers_url_h__
 #define mozilla_dom_workers_url_h__
 
 #include "Workers.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
-#include "nsCycleCollectionParticipant.h"
+#include "mozilla/dom/URLSearchParams.h"
 
 class nsIPrincipal;
 
 namespace mozilla {
 namespace dom {
 class File;
 struct objectURLOptions;
 }
 }
 
 BEGIN_WORKERS_NAMESPACE
 
 class URLProxy;
 
-class URL MOZ_FINAL : public nsISupports
+class URL MOZ_FINAL : public mozilla::dom::URLSearchParamsObserver
 {
+  typedef mozilla::dom::URLSearchParams URLSearchParams;
+
   ~URL();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(URL)
 
   URL(WorkerPrivate* aWorkerPrivate, URLProxy* aURLProxy);
 
@@ -101,30 +103,44 @@ public:
   void GetPathname(nsString& aPathname, ErrorResult& aRv) const;
 
   void SetPathname(const nsAString& aPathname, ErrorResult& aRv);
 
   void GetSearch(nsString& aSearch, ErrorResult& aRv) const;
 
   void SetSearch(const nsAString& aSearch, ErrorResult& aRv);
 
+  URLSearchParams* SearchParams();
+
+  void SetSearchParams(URLSearchParams& aSearchParams);
+
   void GetHash(nsString& aHost, ErrorResult& aRv) const;
 
   void SetHash(const nsAString& aHash, ErrorResult& aRv);
 
   void Stringify(nsString& aRetval, ErrorResult& aRv) const
   {
     GetHref(aRetval, aRv);
   }
 
+  // IURLSearchParamsObserver
+  void URLSearchParamsUpdated(URLSearchParams* aSearchParams) 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
@@ -100,36 +100,106 @@ onmessage = function() {
           is(all[k], checks[i].data[key][k], "value matches");
         }
       }
     }
 
     runTest();
   }
 
+  function testURL() {
+    var url = new URL('http://www.example.net?a=b&c=d');
+    ok(url.searchParams, "URL searchParams exists!");
+    ok(url.searchParams.has('a'), "URL.searchParams.has('a')");
+    is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')");
+    ok(url.searchParams.has('c'), "URL.searchParams.has('c')");
+    is(url.searchParams.get('c'), 'd', "URL.searchParams.get('c')");
+
+    url.searchParams.set('e', 'f');
+    ok(url.href.indexOf('e=f') != 1, 'URL right');
+
+    var u = new URLSearchParams();
+    u.append('foo', 'bar');
+    url.searchParams = u;
+    is(url.searchParams, u, "URL.searchParams is the same object");
+    is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
+    is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
+
+    try {
+      url.searchParams = null;
+      ok(false, "URLSearchParams is not nullable");
+    } catch(e) {
+      ok(true, "URLSearchParams is not nullable");
+    }
+
+    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");
+    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();
+  }
+
   function testEncoding() {
     var encoding = [ [ '1', '1' ],
                      [ 'a b', 'a+b' ],
                      [ '<>', '%3C%3E' ],
                      [ '\u0541', '%D5%81'] ];
 
     for (var i = 0; i < encoding.length; ++i) {
       var a = new URLSearchParams();
       a.set('a', encoding[i][0]);
-      is(a + "", 'a=' + encoding[i][1]);
+
+      var url = new URL('http://www.example.net');
+      url.searchParams = a;
+      is(url.href, 'http://www.example.net/?a=' + encoding[i][1]);
+
+      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,
-    testEncoding
+    testURL,
+    testEncoding,
+    testMultiURL
   ];
 
   function runTest() {
     if (!tests.length) {
       postMessage({type: 'finish' });
       return;
     }
 
--- a/mobile/android/chrome/content/WebcompatReporter.js
+++ b/mobile/android/chrome/content/WebcompatReporter.js
@@ -2,18 +2,16 @@
  * 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/. */
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-Cu.importGlobalProperties(["URL", "URLSearchParams"]);
-
 var WebcompatReporter = {
   menuItem: null,
   menuItemEnabled: null,
   init: function() {
     Services.obs.addObserver(this, "DesktopMode:Change", false);
     Services.obs.addObserver(this, "content-page-shown", false);
     this.addMenuItem();
   },
@@ -73,19 +71,17 @@ var WebcompatReporter = {
         callback: () => this.reportIssue(currentURI)
       }
     };
     NativeWindow.toast.show(message, "long", options);
   },
 
   reportIssue: function(url) {
     let webcompatURL = new URL("http://webcompat.com/");
-    let searchParams = new URLSearchParams();
-    searchParams.append("open", "1");
-    searchParams.append("url", url);
-    webcompatURL.search = searchParams.toString();
+    webcompatURL.searchParams.append("open", "1");
+    webcompatURL.searchParams.append("url", url);
     BrowserApp.addTab(webcompatURL.href);
   }
 };
 
 XPCOMUtils.defineLazyGetter(WebcompatReporter, "strings", function() {
   return Services.strings.createBundle("chrome://browser/locale/webcompatReporter.properties");
 });
--- a/services/fxaccounts/FxAccountsOAuthClient.jsm
+++ b/services/fxaccounts/FxAccountsOAuthClient.jsm
@@ -12,17 +12,17 @@ this.EXPORTED_SYMBOLS = ["FxAccountsOAut
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FxAccountsCommon.js");
 XPCOMUtils.defineLazyModuleGetter(this, "WebChannel",
                                   "resource://gre/modules/WebChannel.jsm");
-Cu.importGlobalProperties(["URL", "URLSearchParams"]);
+Cu.importGlobalProperties(["URL"]);
 
 /**
  * Create a new FxAccountsOAuthClient for browser some service.
  *
  * @param {Object} options Options
  *   @param {Object} options.parameters
  *   Opaque alphanumeric token to be included in verification links
  *     @param {String} options.parameters.client_id
@@ -49,23 +49,23 @@ this.FxAccountsOAuthClient = function(op
   let authorizationEndpoint = options.authorizationEndpoint || "/authorization";
 
   try {
     this._fxaOAuthStartUrl = new URL(this.parameters.oauth_uri + authorizationEndpoint + "?");
   } catch (e) {
     throw new Error("Invalid OAuth Url");
   }
 
-  let params = new URLSearchParams(this._fxaOAuthStartUrl.search.substr(1));
+  let params = this._fxaOAuthStartUrl.searchParams;
   params.append("client_id", this.parameters.client_id);
   params.append("state", this.parameters.state);
   params.append("scope", this.parameters.scope || "");
   params.append("action", this.parameters.action || "signin");
   params.append("webChannelId", this._webChannelId);
-  this._fxaOAuthStartUrl.search = params;
+
 };
 
 this.FxAccountsOAuthClient.prototype = {
   /**
    * Function that gets called once the OAuth flow is complete.
    * The callback will receive null as it's argument if there is a state mismatch or an object with
    * code and state properties otherwise.
    */
--- a/testing/web-platform/meta/url/interfaces.html.ini
+++ b/testing/web-platform/meta/url/interfaces.html.ini
@@ -4,13 +4,8 @@
     expected: FAIL
 
   [URL interface: operation domainToUnicode(ScalarValueString)]
     expected: FAIL
 
   [URLSearchParams interface: existence and properties of interface object]
     expected: FAIL
 
-  [URL interface: attribute searchParams]
-    expected: FAIL
-
-  [URL interface: new URL("http://foo") must inherit property "searchParams" with the proper type (12)]
-    expected: FAIL
--- a/toolkit/components/places/tests/cpp/mock_Link.h
+++ b/toolkit/components/places/tests/cpp/mock_Link.h
@@ -8,16 +8,17 @@
  * This is a mock Link object which can be used in tests.
  */
 
 #ifndef mock_Link_h__
 #define mock_Link_h__
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Link.h"
+#include "mozilla/dom/URLSearchParams.h"
 
 class mock_Link : public mozilla::dom::Link
 {
 public:
   NS_DECL_ISUPPORTS
 
   explicit mock_Link(void (*aHandlerFunction)(nsLinkState),
                      bool aRunNextTest = true)
@@ -111,12 +112,134 @@ Link::GetURI() const
 
 size_t
 Link::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   NS_NOTREACHED("Unexpected call to Link::SizeOfExcludingThis");
   return 0;
 }
 
+void
+Link::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
+{
+  NS_NOTREACHED("Unexpected call to Link::URLSearchParamsUpdated");
+}
+
+void
+Link::UpdateURLSearchParams()
+{
+  NS_NOTREACHED("Unexpected call to Link::UpdateURLSearchParams");
+}
+
+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)
+
+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()
+{
+}
+
+URLSearchParams::~URLSearchParams()
+{
+}
+
+JSObject*
+URLSearchParams::WrapObject(JSContext* aCx)
+{
+  return nullptr;
+}
+
+void
+URLSearchParams::ParseInput(const nsACString& aInput,
+                            URLSearchParamsObserver* aObserver)
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::ParseInput");
+}
+
+void
+URLSearchParams::AddObserver(URLSearchParamsObserver* aObserver)
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::SetObserver");
+}
+
+void
+URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver)
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::SetObserver");
+}
+
+void
+URLSearchParams::Serialize(nsAString& aValue) const
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::Serialize");
+}
+
+void
+URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::Get");
+}
+
+void
+URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString >& aRetval)
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::GetAll");
+}
+
+void
+URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::Set");
+}
+
+void
+URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::Append");
+}
+
+void
+URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue)
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::AppendInternal");
+}
+
+bool
+URLSearchParams::Has(const nsAString& aName)
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::Has");
+  return false;
+}
+
+void
+URLSearchParams::Delete(const nsAString& aName)
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::Delete");
+}
+
+void
+URLSearchParams::DeleteAll()
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::DeleteAll");
+}
+
+void
+URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver)
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::NotifyObservers");
+}
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mock_Link_h__