Bug 1037715 - Implement .searchParams on Location, r=bz, r=ehsan
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 07 Aug 2014 17:45:21 -0700
changeset 198453 9b035f87f26a5f43e50f1feb5e927efedcc1a731
parent 198452 d5a749da75bd57c38226a86de8e1bacb246dcf54
child 198454 2422647220de241b23d6e95ac6bcd594ece846a2
push id27272
push useremorley@mozilla.com
push dateFri, 08 Aug 2014 14:41:27 +0000
treeherdermozilla-central@96a566fa1599 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, ehsan
bugs1037715
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1037715 - Implement .searchParams on Location, r=bz, r=ehsan
content/base/src/Link.cpp
content/base/src/Link.h
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/webidl/Location.webidl
dom/webidl/URLUtils.webidl
dom/workers/URL.cpp
dom/workers/URL.h
toolkit/components/places/tests/cpp/mock_Link.h
--- a/content/base/src/Link.cpp
+++ b/content/base/src/Link.cpp
@@ -591,19 +591,20 @@ Link::SetSearchParams(URLSearchParams& a
   mSearchParams->AddObserver(this);
 
   nsAutoString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
-Link::URLSearchParamsUpdated()
+Link::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
 {
   MOZ_ASSERT(mSearchParams);
+  MOZ_ASSERT(mSearchParams == aSearchParams);
 
   nsString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
 Link::UpdateURLSearchParams()
--- a/content/base/src/Link.h
+++ b/content/base/src/Link.h
@@ -110,17 +110,17 @@ public:
   virtual bool HasDeferredDNSPrefetchRequest() { return true; }
 
   virtual size_t
     SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   bool ElementHasHref() const;
 
   // URLSearchParamsObserver
-  void URLSearchParamsUpdated() MOZ_OVERRIDE;
+  void URLSearchParamsUpdated(URLSearchParams* aSearchParams) MOZ_OVERRIDE;
 
 protected:
   virtual ~Link();
 
   /**
    * Return true if the link has associated URI.
    */
   bool HasURI() const
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -187,16 +187,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"
 
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
 //#define DEBUG_DOCSHELL_FOCUS
 #define DEBUG_PAGE_CACHE
 #endif
 
@@ -1927,16 +1928,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; 
     }
@@ -5343,16 +5362,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
@@ -13182,8 +13206,14 @@ nsDocShell::SetOpenedRemote(nsITabParent
 }
 
 nsITabParent*
 nsDocShell::GetOpenedRemote()
 {
   nsCOMPtr<nsITabParent> openedRemote(do_QueryReferent(mOpenedRemote));
   return openedRemote;
 }
+
+URLSearchParams*
+nsDocShell::GetURLSearchParams()
+{
+  return mURLSearchParams;
+}
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -45,16 +45,17 @@
 #include "nsITabParent.h"
 #include "nsCRT.h"
 #include "prtime.h"
 #include "nsRect.h"
 
 namespace mozilla {
 namespace dom {
 class EventTarget;
+class URLSearchParams;
 }
 }
 
 class nsDocShell;
 class nsDOMNavigationTiming;
 class nsGlobalWindow;
 class nsIController;
 class nsIScrollableFrame;
@@ -758,16 +759,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
@@ -6,24 +6,32 @@
 
 #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;
@@ -41,17 +49,17 @@ interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
  
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(e5fe5c76-e511-4da3-9709-f8294b8dc5ce)]
+[scriptable, builtinclass, uuid(8ccc5a61-e053-4851-ab00-dadab0dffd7f)]
 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.
    *
@@ -977,9 +985,12 @@ interface nsIDocShell : nsIDocShellTreeI
   [noscript,notxpcom,nostdcall] nsITabParent getOpener();
 
   /**
    * 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();
 };
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -336,19 +336,20 @@ URL::GetHost(nsString& aHost, ErrorResul
 
 void
 URL::SetHost(const nsAString& aHost, ErrorResult& aRv)
 {
   mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
 }
 
 void
-URL::URLSearchParamsUpdated()
+URL::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
 {
   MOZ_ASSERT(mSearchParams);
+  MOZ_ASSERT(mSearchParams == aSearchParams);
 
   nsAutoString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
 URL::UpdateURLSearchParams()
--- a/dom/base/URL.h
+++ b/dom/base/URL.h
@@ -116,17 +116,17 @@ public:
   void SetHash(const nsAString& aArg, ErrorResult& aRv);
 
   void Stringify(nsString& aRetval, ErrorResult& aRv) const
   {
     GetHref(aRetval, aRv);
   }
 
   // URLSearchParamsObserver
-  void URLSearchParamsUpdated() MOZ_OVERRIDE;
+  void URLSearchParamsUpdated(URLSearchParams* aSearchParams) MOZ_OVERRIDE;
 
 private:
   nsIURI* GetURI() const
   {
     return mURI;
   }
 
   void CreateSearchParamsIfNeeded();
--- a/dom/base/URLSearchParams.cpp
+++ b/dom/base/URLSearchParams.cpp
@@ -233,16 +233,22 @@ URLSearchParams::AddObserver(URLSearchPa
 void
 URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver)
 {
   MOZ_ASSERT(aObserver);
   mObservers.RemoveElement(aObserver);
 }
 
 void
+URLSearchParams::RemoveObservers()
+{
+  mObservers.Clear();
+}
+
+void
 URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
 {
   nsTArray<nsString>* array;
   if (!mSearchParams.Get(aName, &array)) {
     aRetval.Truncate();
     return;
   }
 
@@ -382,15 +388,15 @@ URLSearchParams::SerializeEnumerator(con
   return PL_DHASH_NEXT;
 }
 
 void
 URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver)
 {
   for (uint32_t i = 0; i < mObservers.Length(); ++i) {
     if (mObservers[i] != aExceptObserver) {
-      mObservers[i]->URLSearchParamsUpdated();
+      mObservers[i]->URLSearchParamsUpdated(this);
     }
   }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/URLSearchParams.h
+++ b/dom/base/URLSearchParams.h
@@ -13,22 +13,24 @@
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 #include "nsISupports.h"
 #include "nsIUnicodeDecoder.h"
 
 namespace mozilla {
 namespace dom {
 
+class URLSearchParams;
+
 class URLSearchParamsObserver : public nsISupports
 {
 public:
   virtual ~URLSearchParamsObserver() {}
 
-  virtual void URLSearchParamsUpdated() = 0;
+  virtual void URLSearchParamsUpdated(URLSearchParams* aFromThis) = 0;
 };
 
 class URLSearchParams MOZ_FINAL : public nsISupports,
                                   public nsWrapperCache
 {
   ~URLSearchParams();
 
 public:
@@ -54,16 +56,17 @@ public:
   Constructor(const GlobalObject& aGlobal, URLSearchParams& aInit,
               ErrorResult& aRv);
 
   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);
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -63,26 +63,43 @@ nsLocation::nsLocation(nsPIDOMWindow* aW
   MOZ_ASSERT(mInnerWindow->IsInnerWindow());
   SetIsDOMBinding();
 
   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(nsISupports)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMLocation)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsLocation, mInnerWindow)
+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)
 
 void
 nsLocation::SetDocShell(nsIDocShell *aDocShell)
 {
    mDocShell = do_GetWeakReference(aDocShell);
 }
@@ -854,16 +871,27 @@ nsLocation::GetSearch(nsAString& aSearch
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLocation::SetSearch(const nsAString& aSearch)
 {
+  nsresult rv = SetSearchInternal(aSearch);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsLocation::SetSearchInternal(const nsAString& aSearch)
+{
   if (!CallerSubsumes())
     return NS_ERROR_DOM_SECURITY_ERR;
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetWritableURI(getter_AddRefs(uri));
 
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (NS_WARN_IF(NS_FAILED(rv) || !url)) {
@@ -1031,8 +1059,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,36 +9,39 @@
 
 #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(nsLocation)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsLocation,
+                                                         nsIDOMLocation)
 
   void SetDocShell(nsIDocShell *aDocShell);
   nsIDocShell *GetDocShell();
 
   // nsIDOMLocation
   NS_DECL_NSIDOMLOCATION
 
   // WebIDL API:
@@ -113,16 +116,21 @@ public:
   void GetSearch(nsAString& aSeach, ErrorResult& aError)
   {
     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);
   }
@@ -131,19 +139,28 @@ 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,
                            bool aReplace);
@@ -151,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
@@ -42,16 +42,17 @@ skip-if = buildapp == 'mulet' || buildap
 [test_gsp-quirks.html]
 [test_gsp-standards.html]
 [test_getFeature_with_perm.html]
 [test_getFeature_without_perm.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/webidl/Location.webidl
+++ b/dom/webidl/Location.webidl
@@ -17,9 +17,9 @@ interface Location {
   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 URLUtilsNoSearchParams;
+Location implements URLUtils;
--- a/dom/webidl/URLUtils.webidl
+++ b/dom/webidl/URLUtils.webidl
@@ -10,17 +10,17 @@
  * and related or neighboring rights to this work. In addition, as of 17
  * February 2013, the editors have made this specification available under
  * the Open Web Foundation Agreement Version 1.0, which is available at
  * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
  */
 
 [NoInterfaceObject,
  Exposed=(Window, Worker)]
-interface URLUtilsNoSearchParams {
+interface URLUtils {
   // Bug 824857: no support for stringifier attributes yet.
   //  stringifier attribute DOMString href;
   [Throws, CrossOriginWritable=Location]
            attribute DOMString href;
   [Throws]
   readonly attribute DOMString origin;
 
   [Throws]
@@ -34,23 +34,18 @@ interface URLUtilsNoSearchParams {
   [Throws]
            attribute DOMString hostname;
   [Throws]
            attribute DOMString port;
   [Throws]
            attribute DOMString pathname;
   [Throws]
            attribute DOMString search;
-  // searchParams should go here once Location implements it.  See bug 1037715.
+
+           attribute URLSearchParams searchParams;
+
   [Throws]
            attribute DOMString hash;
 
   // Bug 824857 should remove this.
   [Throws]
   stringifier;
 };
-
-[NoInterfaceObject,
- Exposed=(Window, Worker)]
-interface URLUtils : URLUtilsNoSearchParams
-{
-           attribute URLSearchParams searchParams;
-};
--- a/dom/workers/URL.cpp
+++ b/dom/workers/URL.cpp
@@ -889,19 +889,20 @@ URL::RevokeObjectURL(const GlobalObject&
     new RevokeURLRunnable(workerPrivate, aUrl);
 
   if (!runnable->Dispatch(cx)) {
     JS_ReportPendingException(cx);
   }
 }
 
 void
-URL::URLSearchParamsUpdated()
+URL::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
 {
   MOZ_ASSERT(mSearchParams);
+  MOZ_ASSERT(mSearchParams == aSearchParams);
 
   nsString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
 URL::UpdateURLSearchParams()
--- a/dom/workers/URL.h
+++ b/dom/workers/URL.h
@@ -114,17 +114,17 @@ public:
   void SetHash(const nsAString& aHash, ErrorResult& aRv);
 
   void Stringify(nsString& aRetval, ErrorResult& aRv) const
   {
     GetHref(aRetval, aRv);
   }
 
   // IURLSearchParamsObserver
-  void URLSearchParamsUpdated() MOZ_OVERRIDE;
+  void URLSearchParamsUpdated(URLSearchParams* aSearchParams) MOZ_OVERRIDE;
 
 private:
   URLProxy* GetURLProxy() const
   {
     return mURLProxy;
   }
 
   void CreateSearchParamsIfNeeded();
--- a/toolkit/components/places/tests/cpp/mock_Link.h
+++ b/toolkit/components/places/tests/cpp/mock_Link.h
@@ -113,17 +113,17 @@ Link::GetURI() const
 size_t
 Link::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   NS_NOTREACHED("Unexpected call to Link::SizeOfExcludingThis");
   return 0;
 }
 
 void
-Link::URLSearchParamsUpdated()
+Link::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
 {
   NS_NOTREACHED("Unexpected call to Link::URLSearchParamsUpdated");
 }
 
 void
 Link::UpdateURLSearchParams()
 {
   NS_NOTREACHED("Unexpected call to Link::UpdateURLSearchParams");