From 3dede2f23401438c7e1e5a502118a6a696597cb2 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 23 Oct 2014 15:00:30 +0200 Subject: [PATCH] Backed out changeset 2340c1d21e94 (bug 1082734) for w4 test failures --- .../loop/test/mochitest/loop_fxa.sjs | 5 +- content/base/src/Link.cpp | 87 ++++++++++ content/base/src/Link.h | 21 ++- .../html/content/src/HTMLAnchorElement.cpp | 2 + content/html/content/src/HTMLAreaElement.cpp | 2 + content/html/content/src/HTMLLinkElement.cpp | 2 + content/svg/content/src/SVGAElement.cpp | 2 + docshell/base/nsDocShell.cpp | 30 ++++ docshell/base/nsDocShell.h | 4 + docshell/base/nsIDocShell.idl | 12 +- dom/base/URL.cpp | 76 +++++++++ dom/base/URL.h | 17 +- dom/base/URLSearchParams.cpp | 57 ++++++- dom/base/URLSearchParams.h | 20 ++- dom/base/nsLocation.cpp | 120 ++++++++++++++ dom/base/nsLocation.h | 14 ++ dom/base/test/mochitest.ini | 1 + dom/base/test/test_location_searchParams.html | 89 +++++++++++ dom/base/test/test_urlSearchParams.html | 150 ++++++++++++++++-- dom/webidl/Location.webidl | 2 +- dom/webidl/URLUtils.webidl | 2 +- dom/workers/URL.cpp | 66 +++++++- dom/workers/URL.h | 20 ++- dom/workers/test/urlSearchParams_worker.js | 74 ++++++++- .../chrome/content/WebcompatReporter.js | 8 +- services/fxaccounts/FxAccountsOAuthClient.jsm | 6 +- .../components/places/tests/cpp/mock_Link.h | 123 ++++++++++++++ 27 files changed, 974 insertions(+), 38 deletions(-) create mode 100644 dom/base/test/test_location_searchParams.html diff --git a/browser/components/loop/test/mochitest/loop_fxa.sjs b/browser/components/loop/test/mochitest/loop_fxa.sjs index 5977f79588f3..c340dfa7d10c 100644 --- a/browser/components/loop/test/mochitest/loop_fxa.sjs +++ b/browser/components/loop/test/mochitest/loop_fxa.sjs @@ -11,7 +11,7 @@ const REQUIRED_PARAMS = ["client_id", "content_uri", "oauth_uri", "profile_uri", 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. @@ -225,8 +225,7 @@ function delete_registration(request, response) { // 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"); diff --git a/content/base/src/Link.cpp b/content/base/src/Link.cpp index 9a16e66a7ddc..3b2926324ad1 100644 --- a/content/base/src/Link.cpp +++ b/content/base/src/Link.cpp @@ -214,6 +214,13 @@ Link::SetPathname(const nsAString &aPathname, ErrorResult& aError) void Link::SetSearch(const nsAString& aSearch, ErrorResult& aError) +{ + SetSearchInternal(aSearch); + UpdateURLSearchParams(); +} + +void +Link::SetSearchInternal(const nsAString& aSearch) { nsCOMPtr uri(GetURIToMutate()); nsCOMPtr url(do_QueryInterface(uri)); @@ -471,6 +478,7 @@ Link::ResetLinkState(bool aNotify, bool aHasHref) // If we've cached the URI, reset always invalidates it. mCachedURI = nullptr; + UpdateURLSearchParams(); // Update our state back to the default. mLinkState = defaultState; @@ -559,5 +567,84 @@ Link::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const 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 uri(GetURI()); + 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 +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 diff --git a/content/base/src/Link.h b/content/base/src/Link.h index 1c850049f8e4..0d98316f36f2 100644 --- a/content/base/src/Link.h +++ b/content/base/src/Link.h @@ -13,6 +13,7 @@ #include "mozilla/IHistory.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/dom/URLSearchParams.h" #include "nsIContent.h" // for nsLinkState namespace mozilla { @@ -27,7 +28,7 @@ class Element; { 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) @@ -63,6 +64,7 @@ public: 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); @@ -73,6 +75,7 @@ public: 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); @@ -111,6 +114,9 @@ public: bool ElementHasHref() const; + // URLSearchParamsObserver + void URLSearchParamsUpdated(URLSearchParams* aSearchParams) MOZ_OVERRIDE; + protected: virtual ~Link(); @@ -129,6 +135,12 @@ protected: 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 @@ -139,6 +151,10 @@ private: already_AddRefed GetURIToMutate(); void SetHrefAttribute(nsIURI *aURI); + void CreateSearchParamsIfNeeded(); + + void SetSearchInternal(const nsAString& aSearch); + mutable nsCOMPtr mCachedURI; Element * const mElement; @@ -152,6 +168,9 @@ private: bool mNeedsRegistration; bool mRegistered; + +protected: + nsRefPtr mSearchParams; }; NS_DEFINE_STATIC_IID_ACCESSOR(Link, MOZILLA_DOM_LINK_IMPLEMENTATION_IID) diff --git a/content/html/content/src/HTMLAnchorElement.cpp b/content/html/content/src/HTMLAnchorElement.cpp index ce36d8243dfc..0672b45aeaa6 100644 --- a/content/html/content/src/HTMLAnchorElement.cpp +++ b/content/html/content/src/HTMLAnchorElement.cpp @@ -56,11 +56,13 @@ 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 diff --git a/content/html/content/src/HTMLAreaElement.cpp b/content/html/content/src/HTMLAreaElement.cpp index 4a84df2fea0b..a427d3a19bc4 100644 --- a/content/html/content/src/HTMLAreaElement.cpp +++ b/content/html/content/src/HTMLAreaElement.cpp @@ -40,11 +40,13 @@ 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 diff --git a/content/html/content/src/HTMLLinkElement.cpp b/content/html/content/src/HTMLLinkElement.cpp index c63715c43444..502ac7dbf492 100644 --- a/content/html/content/src/HTMLLinkElement.cpp +++ b/content/html/content/src/HTMLLinkElement.cpp @@ -50,6 +50,7 @@ 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 @@ -57,6 +58,7 @@ 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 diff --git a/content/svg/content/src/SVGAElement.cpp b/content/svg/content/src/SVGAElement.cpp index fc07c574eb57..05f289e12c00 100644 --- a/content/svg/content/src/SVGAElement.cpp +++ b/content/svg/content/src/SVGAElement.cpp @@ -48,10 +48,12 @@ 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) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 098be8eba0cd..2c088cc6f63b 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -193,6 +193,7 @@ #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" @@ -1959,6 +1960,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 @@ -5612,6 +5631,11 @@ nsDocShell::Destroy() mParentWidget = nullptr; mCurrentURI = nullptr; + if (mURLSearchParams) { + mURLSearchParams->RemoveObservers(); + mURLSearchParams = nullptr; + } + if (mScriptGlobal) { mScriptGlobal->DetachFromDocShell(); mScriptGlobal = nullptr; @@ -13530,6 +13554,12 @@ nsDocShell::GetOpenedRemote() return openedRemote; } +URLSearchParams* +nsDocShell::GetURLSearchParams() +{ + return mURLSearchParams; +} + void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString &aProvider, const nsString &aKeyword) { diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index df056f4049ca..98c97aa3d0a5 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -57,6 +57,7 @@ namespace mozilla { namespace dom { class EventTarget; +class URLSearchParams; } } @@ -796,6 +797,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 3f25a8626f9b..4aaa717d0837 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -12,6 +12,12 @@ class nsPresContext; class nsIPresShell; +namespace mozilla { +namespace dom { +class URLSearchParams; +} +} + %} /** @@ -20,6 +26,7 @@ class nsIPresShell; [ptr] native nsPresContext(nsPresContext); [ptr] native nsIPresShell(nsIPresShell); +[ptr] native URLSearchParams(mozilla::dom::URLSearchParams); interface nsIURI; interface nsIChannel; @@ -47,7 +54,7 @@ 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 { /** @@ -1016,6 +1023,9 @@ 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(); + /** * This attribute determines whether a document which is not about:blank has * already be loaded by this docShell. diff --git a/dom/base/URL.cpp b/dom/base/URL.cpp index 306516062100..0b98c295fa8c 100644 --- a/dom/base/URL.cpp +++ b/dom/base/URL.cpp @@ -24,9 +24,14 @@ 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) @@ -228,6 +233,7 @@ URL::SetHref(const nsAString& aHref, ErrorResult& aRv) } mURI = uri; + UpdateURLSearchParams(); } void @@ -342,6 +348,36 @@ 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 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 { @@ -441,6 +477,13 @@ URL::GetSearch(nsString& aSearch, ErrorResult& aRv) const void URL::SetSearch(const nsAString& aSearch, ErrorResult& aRv) +{ + SetSearchInternal(aSearch); + UpdateURLSearchParams(); +} + +void +URL::SetSearchInternal(const nsAString& aSearch) { nsCOMPtr url(do_QueryInterface(mURI)); if (!url) { @@ -451,6 +494,29 @@ URL::SetSearch(const nsAString& aSearch, ErrorResult& aRv) 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 { @@ -479,5 +545,15 @@ bool IsChromeURI(nsIURI* aURI) return false; } +void +URL::CreateSearchParamsIfNeeded() +{ + if (!mSearchParams) { + mSearchParams = new URLSearchParams(); + mSearchParams->AddObserver(this); + UpdateURLSearchParams(); + } +} + } } diff --git a/dom/base/URL.h b/dom/base/URL.h index 1ae6e96cc15a..54a82a9e6e4a 100644 --- a/dom/base/URL.h +++ b/dom/base/URL.h @@ -6,6 +6,7 @@ #define URL_h___ #include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/URLSearchParams.h" #include "nsCycleCollectionParticipant.h" #include "nsAutoPtr.h" #include "nsString.h" @@ -31,7 +32,7 @@ namespace workers { class URLProxy; } -class URL MOZ_FINAL : public nsISupports +class URL MOZ_FINAL : public URLSearchParamsObserver { ~URL() {} @@ -108,6 +109,10 @@ public: 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); @@ -117,12 +122,21 @@ public: 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, @@ -131,6 +145,7 @@ private: ErrorResult& aError); nsCOMPtr mURI; + nsRefPtr mSearchParams; friend class mozilla::dom::workers::URLProxy; }; diff --git a/dom/base/URLSearchParams.cpp b/dom/base/URLSearchParams.cpp index c758bd69b36c..0b9cd4cf03c0 100644 --- a/dom/base/URLSearchParams.cpp +++ b/dom/base/URLSearchParams.cpp @@ -11,7 +11,7 @@ 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) @@ -41,7 +41,7 @@ URLSearchParams::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) { nsRefPtr sp = new URLSearchParams(); - sp->ParseInput(NS_ConvertUTF16toUTF8(aInit)); + sp->ParseInput(NS_ConvertUTF16toUTF8(aInit), nullptr); return sp.forget(); } @@ -56,7 +56,8 @@ URLSearchParams::Constructor(const GlobalObject& aGlobal, } void -URLSearchParams::ParseInput(const nsACString& aInput) +URLSearchParams::ParseInput(const nsACString& aInput, + URLSearchParamsObserver* aObserver) { // Remove all the existing data before parsing a new input. DeleteAll(); @@ -104,8 +105,10 @@ URLSearchParams::ParseInput(const nsACString& aInput) nsAutoString decodedValue; DecodeString(value, decodedValue); - Append(decodedName, decodedValue); + AppendInternal(decodedName, decodedValue); } + + NotifyObservers(aObserver); } void @@ -204,6 +207,27 @@ URLSearchParams::ConvertString(const nsACString& aInput, nsAString& aOutput) } } +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) { @@ -254,10 +278,19 @@ URLSearchParams::Set(const nsAString& aName, const nsAString& aValue) } 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; @@ -279,13 +312,19 @@ URLSearchParams::Has(const nsAString& aName) 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 @@ -339,5 +378,15 @@ URLSearchParams::Serialize(nsAString& aValue) const } } +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 diff --git a/dom/base/URLSearchParams.h b/dom/base/URLSearchParams.h index f62a431c4208..ec1afad5505d 100644 --- a/dom/base/URLSearchParams.h +++ b/dom/base/URLSearchParams.h @@ -18,6 +18,14 @@ 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 { @@ -46,7 +54,12 @@ public: 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; @@ -68,11 +81,15 @@ public: } 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; @@ -81,6 +98,7 @@ private: nsTArray mSearchParams; + nsTArray> mObservers; nsCOMPtr mDecoder; }; diff --git a/dom/base/nsLocation.cpp b/dom/base/nsLocation.cpp index 5e77de6bd1ba..2146cf3a688f 100644 --- a/dom/base/nsLocation.cpp +++ b/dom/base/nsLocation.cpp @@ -61,6 +61,7 @@ nsLocation::nsLocation(nsPIDOMWindow* aWindow, nsIDocShell *aDocShell) nsLocation::~nsLocation() { + RemoveURLSearchParams(); } // QueryInterface implementation for nsLocation @@ -73,11 +74,14 @@ 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 @@ -1042,3 +1046,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 0d0b04b9009f..8bc58f6f3543 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; @@ -120,6 +122,10 @@ public: aError = SetSearch(aSeach); } + mozilla::dom::URLSearchParams* SearchParams(); + + void SetSearchParams(mozilla::dom::URLSearchParams& aSearchParams); + void GetHash(nsAString& aHash, ErrorResult& aError) { aError = GetHash(aHash); @@ -138,10 +144,17 @@ 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 @@ -160,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 795c82b84ddc..96e0919beb78 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -50,6 +50,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 000000000000..0e7a25d3f926 --- /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/base/test/test_urlSearchParams.html b/dom/base/test/test_urlSearchParams.html index b586721eb04c..46fa37ea5bae 100644 --- a/dom/base/test/test_urlSearchParams.html +++ b/dom/base/test/test_urlSearchParams.html @@ -113,6 +113,81 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836 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' ], @@ -122,12 +197,57 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836 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"); @@ -158,8 +278,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836 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(); } @@ -172,20 +296,20 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836 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"); @@ -193,10 +317,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836 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(); @@ -206,7 +330,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836 testSimpleURLSearchParams, testCopyURLSearchParams, testParserURLSearchParams, + testURL, + function() { testElement(document.getElementById('anchor')) }, + function() { testElement(document.getElementById('area')) }, testEncoding, + testMultiURL, testOrdering, testDelete, testGetNULL, diff --git a/dom/webidl/Location.webidl b/dom/webidl/Location.webidl index 2954dab17c37..59999602010f 100644 --- a/dom/webidl/Location.webidl +++ b/dom/webidl/Location.webidl @@ -21,5 +21,5 @@ interface Location { [Throws] void reload(optional boolean forceget = false); }; - +// No support for .searchParams on Location yet. See bug 1037715. Location implements URLUtils; diff --git a/dom/webidl/URLUtils.webidl b/dom/webidl/URLUtils.webidl index 77afbb1df41b..09c36f296c5a 100644 --- a/dom/webidl/URLUtils.webidl +++ b/dom/webidl/URLUtils.webidl @@ -40,7 +40,7 @@ interface URLUtils { [Throws] attribute ScalarValueString search; - // Bug 1082734 - attribute URLSearchParams searchParams; + attribute URLSearchParams searchParams; [Throws] attribute ScalarValueString hash; diff --git a/dom/workers/URL.cpp b/dom/workers/URL.cpp index 38050265c8b1..0961edf93984 100644 --- a/dom/workers/URL.cpp +++ b/dom/workers/URL.cpp @@ -12,6 +12,7 @@ #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" @@ -459,7 +460,7 @@ private: 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. @@ -570,6 +571,8 @@ URL::SetHref(const nsAString& aHref, ErrorResult& aRv) if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) { JS_ReportPendingException(mWorkerPrivate->GetJSContext()); } + + UpdateURLSearchParams(); } void @@ -773,6 +776,13 @@ URL::GetSearch(nsString& aSearch, ErrorResult& aRv) const void URL::SetSearch(const nsAString& aSearch, ErrorResult& aRv) +{ + SetSearchInternal(aSearch); + UpdateURLSearchParams(); +} + +void +URL::SetSearchInternal(const nsAString& aSearch) { ErrorResult rv; nsRefPtr runnable = @@ -784,6 +794,28 @@ URL::SetSearch(const nsAString& aSearch, ErrorResult& aRv) } } +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 { @@ -854,4 +886,36 @@ URL::RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl) } } +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 diff --git a/dom/workers/URL.h b/dom/workers/URL.h index 94ddbc044de4..305f983eec24 100644 --- a/dom/workers/URL.h +++ b/dom/workers/URL.h @@ -11,7 +11,7 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/BindingDeclarations.h" -#include "nsCycleCollectionParticipant.h" +#include "mozilla/dom/URLSearchParams.h" class nsIPrincipal; @@ -26,8 +26,10 @@ 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: @@ -106,6 +108,10 @@ public: 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); @@ -115,14 +121,24 @@ public: 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 mURLProxy; + nsRefPtr mSearchParams; }; END_WORKERS_NAMESPACE diff --git a/dom/workers/test/urlSearchParams_worker.js b/dom/workers/test/urlSearchParams_worker.js index 1de1fc28a6c6..6bc0ba770728 100644 --- a/dom/workers/test/urlSearchParams_worker.js +++ b/dom/workers/test/urlSearchParams_worker.js @@ -105,6 +105,42 @@ onmessage = function() { 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' ], @@ -114,17 +150,51 @@ onmessage = function() { 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() { diff --git a/mobile/android/chrome/content/WebcompatReporter.js b/mobile/android/chrome/content/WebcompatReporter.js index 576455b75c1d..264553e62898 100644 --- a/mobile/android/chrome/content/WebcompatReporter.js +++ b/mobile/android/chrome/content/WebcompatReporter.js @@ -7,8 +7,6 @@ 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, @@ -78,10 +76,8 @@ var WebcompatReporter = { 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); } }; diff --git a/services/fxaccounts/FxAccountsOAuthClient.jsm b/services/fxaccounts/FxAccountsOAuthClient.jsm index b9754a7fd3a1..cf5572273bc2 100644 --- a/services/fxaccounts/FxAccountsOAuthClient.jsm +++ b/services/fxaccounts/FxAccountsOAuthClient.jsm @@ -17,7 +17,7 @@ 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. @@ -54,13 +54,13 @@ this.FxAccountsOAuthClient = function(options) { 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 = { diff --git a/toolkit/components/places/tests/cpp/mock_Link.h b/toolkit/components/places/tests/cpp/mock_Link.h index d87bc34f92d1..fdd0386cd596 100644 --- a/toolkit/components/places/tests/cpp/mock_Link.h +++ b/toolkit/components/places/tests/cpp/mock_Link.h @@ -13,6 +13,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/dom/Link.h" +#include "mozilla/dom/URLSearchParams.h" class mock_Link : public mozilla::dom::Link { @@ -116,6 +117,128 @@ Link::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const 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& 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