diff --git a/content/base/src/Link.cpp b/content/base/src/Link.cpp index 21f10cf3c1d..9506551441e 100644 --- a/content/base/src/Link.cpp +++ b/content/base/src/Link.cpp @@ -596,9 +596,10 @@ Link::SetSearchParams(URLSearchParams& aSearchParams) } void -Link::URLSearchParamsUpdated() +Link::URLSearchParamsUpdated(URLSearchParams* aSearchParams) { MOZ_ASSERT(mSearchParams); + MOZ_ASSERT(mSearchParams == aSearchParams); nsString search; mSearchParams->Serialize(search); diff --git a/content/base/src/Link.h b/content/base/src/Link.h index 9ade6e49527..ae004767292 100644 --- a/content/base/src/Link.h +++ b/content/base/src/Link.h @@ -115,7 +115,7 @@ public: bool ElementHasHref() const; // URLSearchParamsObserver - void URLSearchParamsUpdated() MOZ_OVERRIDE; + void URLSearchParamsUpdated(URLSearchParams* aSearchParams) MOZ_OVERRIDE; protected: virtual ~Link(); diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index c49a33d927c..bca80b07ac2 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -192,6 +192,7 @@ #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); @@ -1932,6 +1933,24 @@ nsDocShell::SetCurrentURI(nsIURI *aURI, nsIRequest *aRequest, 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 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 @@ -5348,6 +5367,11 @@ nsDocShell::Destroy() mParentWidget = nullptr; mCurrentURI = nullptr; + if (mURLSearchParams) { + mURLSearchParams->RemoveObservers(); + mURLSearchParams = nullptr; + } + if (mScriptGlobal) { mScriptGlobal->DetachFromDocShell(); mScriptGlobal = nullptr; @@ -13187,3 +13211,9 @@ nsDocShell::GetOpenedRemote() nsCOMPtr openedRemote(do_QueryReferent(mOpenedRemote)); return openedRemote; } + +URLSearchParams* +nsDocShell::GetURLSearchParams() +{ + return mURLSearchParams; +} diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 9b697bff8a7..82b93bcfb7c 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -50,6 +50,7 @@ namespace mozilla { namespace dom { class EventTarget; +class URLSearchParams; } } @@ -763,6 +764,9 @@ protected: nsCOMPtr mFailedChannel; uint32_t mFailedLoadType; + // window.location.searchParams is updated in sync with this object. + nsRefPtr 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. diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl index f9daf4e7190..18efa0c96c0 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -11,6 +11,13 @@ #include "js/TypeDecls.h" class nsPresContext; class nsIPresShell; + +namespace mozilla { +namespace dom { +class URLSearchParams; +} +} + %} /** @@ -19,6 +26,7 @@ class nsIPresShell; [ptr] native nsPresContext(nsPresContext); [ptr] native nsIPresShell(nsIPresShell); +[ptr] native URLSearchParams(mozilla::dom::URLSearchParams); interface nsIURI; interface nsIChannel; @@ -46,7 +54,7 @@ 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 { /** @@ -982,4 +990,7 @@ interface nsIDocShell : nsIDocShellTreeItem */ [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(); }; diff --git a/dom/base/URL.cpp b/dom/base/URL.cpp index 80fe487d847..7a73c265e7e 100644 --- a/dom/base/URL.cpp +++ b/dom/base/URL.cpp @@ -341,9 +341,10 @@ URL::SetHost(const nsAString& aHost, ErrorResult& aRv) } void -URL::URLSearchParamsUpdated() +URL::URLSearchParamsUpdated(URLSearchParams* aSearchParams) { MOZ_ASSERT(mSearchParams); + MOZ_ASSERT(mSearchParams == aSearchParams); nsAutoString search; mSearchParams->Serialize(search); diff --git a/dom/base/URL.h b/dom/base/URL.h index 1ed7b85dcbc..23cfa85e0d8 100644 --- a/dom/base/URL.h +++ b/dom/base/URL.h @@ -121,7 +121,7 @@ public: } // URLSearchParamsObserver - void URLSearchParamsUpdated() MOZ_OVERRIDE; + void URLSearchParamsUpdated(URLSearchParams* aSearchParams) MOZ_OVERRIDE; private: nsIURI* GetURI() const diff --git a/dom/base/URLSearchParams.cpp b/dom/base/URLSearchParams.cpp index 7551a48747d..dc9d22e9e81 100644 --- a/dom/base/URLSearchParams.cpp +++ b/dom/base/URLSearchParams.cpp @@ -237,6 +237,12 @@ URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver) mObservers.RemoveElement(aObserver); } +void +URLSearchParams::RemoveObservers() +{ + mObservers.Clear(); +} + void URLSearchParams::Get(const nsAString& aName, nsString& aRetval) { @@ -387,7 +393,7 @@ URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver) { for (uint32_t i = 0; i < mObservers.Length(); ++i) { if (mObservers[i] != aExceptObserver) { - mObservers[i]->URLSearchParamsUpdated(); + mObservers[i]->URLSearchParamsUpdated(this); } } } diff --git a/dom/base/URLSearchParams.h b/dom/base/URLSearchParams.h index 1424d36aed3..e141f53ddd2 100644 --- a/dom/base/URLSearchParams.h +++ b/dom/base/URLSearchParams.h @@ -18,12 +18,14 @@ 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, @@ -59,6 +61,7 @@ public: void AddObserver(URLSearchParamsObserver* aObserver); void RemoveObserver(URLSearchParamsObserver* aObserver); + void RemoveObservers(); void Serialize(nsAString& aValue) const; diff --git a/dom/base/nsLocation.cpp b/dom/base/nsLocation.cpp index 7012d500176..d16e6c72080 100644 --- a/dom/base/nsLocation.cpp +++ b/dom/base/nsLocation.cpp @@ -68,16 +68,33 @@ nsLocation::nsLocation(nsPIDOMWindow* aWindow, nsIDocShell *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) @@ -858,6 +875,17 @@ nsLocation::GetSearch(nsAString& aSearch) 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; @@ -1036,3 +1064,119 @@ nsLocation::WrapObject(JSContext* aCx) { return LocationBinding::Wrap(aCx, this); } + +URLSearchParams* +nsLocation::GetDocShellSearchParams() +{ + nsCOMPtr 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 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 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 uri; + nsresult rv = GetURI(getter_AddRefs(uri)); + if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!uri)) { + return; + } + + nsCOMPtr 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 docShellSearchParams = GetDocShellSearchParams(); + if (docShellSearchParams) { + docShellSearchParams->RemoveObserver(this); + } + } +} diff --git a/dom/base/nsLocation.h b/dom/base/nsLocation.h index f3fc3b4d73e..8bc58f6f354 100644 --- a/dom/base/nsLocation.h +++ b/dom/base/nsLocation.h @@ -14,6 +14,7 @@ #include "nsCycleCollectionParticipant.h" #include "js/TypeDecls.h" #include "mozilla/ErrorResult.h" +#include "mozilla/dom/URLSearchParams.h" #include "nsPIDOMWindow.h" class nsIURI; @@ -26,6 +27,7 @@ class nsIDocShellLoadInfo; class nsLocation MOZ_FINAL : public nsIDOMLocation , public nsWrapperCache + , public mozilla::dom::URLSearchParamsObserver { typedef mozilla::ErrorResult ErrorResult; @@ -33,7 +35,8 @@ 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(); @@ -118,6 +121,11 @@ public: { aError = SetSearch(aSeach); } + + mozilla::dom::URLSearchParams* SearchParams(); + + void SetSearchParams(mozilla::dom::URLSearchParams& aSearchParams); + void GetHash(nsAString& aHash, ErrorResult& aError) { aError = GetHash(aHash); @@ -136,9 +144,18 @@ public: } 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. @@ -156,6 +173,7 @@ protected: nsString mCachedHash; nsCOMPtr mInnerWindow; + nsRefPtr mSearchParams; nsWeakPtr mDocShell; }; diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 598997096d0..c988349f39f 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -47,6 +47,7 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e1 [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] diff --git a/dom/base/test/test_location_searchParams.html b/dom/base/test/test_location_searchParams.html new file mode 100644 index 00000000000..0e7a25d3f92 --- /dev/null +++ b/dom/base/test/test_location_searchParams.html @@ -0,0 +1,89 @@ + + + + + + + Test for Bug 1037715 + + + + + Mozilla Bug 1037715 + + + + + diff --git a/dom/webidl/Location.webidl b/dom/webidl/Location.webidl index 7cc105890d0..59999602010 100644 --- a/dom/webidl/Location.webidl +++ b/dom/webidl/Location.webidl @@ -22,4 +22,4 @@ interface Location { void reload(optional boolean forceget = false); }; // No support for .searchParams on Location yet. See bug 1037715. -Location implements URLUtilsNoSearchParams; +Location implements URLUtils; diff --git a/dom/webidl/URLUtils.webidl b/dom/webidl/URLUtils.webidl index 05cb644e6c5..ad321622014 100644 --- a/dom/webidl/URLUtils.webidl +++ b/dom/webidl/URLUtils.webidl @@ -15,7 +15,7 @@ [NoInterfaceObject, Exposed=(Window, Worker)] -interface URLUtilsNoSearchParams { +interface URLUtils { // Bug 824857: no support for stringifier attributes yet. // stringifier attribute DOMString href; [Throws, CrossOriginWritable=Location] @@ -39,7 +39,9 @@ interface URLUtilsNoSearchParams { 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; @@ -47,10 +49,3 @@ interface URLUtilsNoSearchParams { [Throws] stringifier; }; - -[NoInterfaceObject, - Exposed=(Window, Worker)] -interface URLUtils : URLUtilsNoSearchParams -{ - attribute URLSearchParams searchParams; -}; diff --git a/dom/workers/URL.cpp b/dom/workers/URL.cpp index 6c031315821..9a44abb0fc8 100644 --- a/dom/workers/URL.cpp +++ b/dom/workers/URL.cpp @@ -894,9 +894,10 @@ URL::RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl) } void -URL::URLSearchParamsUpdated() +URL::URLSearchParamsUpdated(URLSearchParams* aSearchParams) { MOZ_ASSERT(mSearchParams); + MOZ_ASSERT(mSearchParams == aSearchParams); nsString search; mSearchParams->Serialize(search); diff --git a/dom/workers/URL.h b/dom/workers/URL.h index 50492b61b04..7a7c4755982 100644 --- a/dom/workers/URL.h +++ b/dom/workers/URL.h @@ -119,7 +119,7 @@ public: } // IURLSearchParamsObserver - void URLSearchParamsUpdated() MOZ_OVERRIDE; + void URLSearchParamsUpdated(URLSearchParams* aSearchParams) MOZ_OVERRIDE; private: URLProxy* GetURLProxy() const diff --git a/toolkit/components/places/tests/cpp/mock_Link.h b/toolkit/components/places/tests/cpp/mock_Link.h index 32d53a7ce09..7f049fbebd1 100644 --- a/toolkit/components/places/tests/cpp/mock_Link.h +++ b/toolkit/components/places/tests/cpp/mock_Link.h @@ -118,7 +118,7 @@ Link::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const } void -Link::URLSearchParamsUpdated() +Link::URLSearchParamsUpdated(URLSearchParams* aSearchParams) { NS_NOTREACHED("Unexpected call to Link::URLSearchParamsUpdated"); }