diff --git a/src/AngleSharp.Core.Tests/Urls/UrlApi.cs b/src/AngleSharp.Core.Tests/Urls/UrlApi.cs index 503dfe334..981933391 100644 --- a/src/AngleSharp.Core.Tests/Urls/UrlApi.cs +++ b/src/AngleSharp.Core.Tests/Urls/UrlApi.cs @@ -147,6 +147,20 @@ public void UrlParamsAreLive() Assert.AreEqual("foo=bar", url.Query); } + [Test] + public void UrlQueryDoesNotDependOnParams() + { + var url = new Url("https://florian-rappl.de?qxz=bar"); + Assert.AreEqual("bar", url.SearchParams.Get("qxz")); + Assert.AreEqual(true, url.SearchParams.Has("qxz")); + Assert.AreEqual(null, url.SearchParams.Get("foo")); + url.Query = "foo"; + Assert.AreEqual(null, url.SearchParams.Get("qxz")); + Assert.AreEqual(false, url.SearchParams.Has("qxz")); + Assert.AreEqual("", url.SearchParams.Get("foo")); + Assert.AreEqual("foo", url.Query); + } + [Test] public void UrlSearchAssigningStringWithoutQuestion() { diff --git a/src/AngleSharp/Dom/Url.cs b/src/AngleSharp/Dom/Url.cs index f3b20d92c..dcb6e6097 100644 --- a/src/AngleSharp/Dom/Url.cs +++ b/src/AngleSharp/Dom/Url.cs @@ -393,7 +393,7 @@ public String Protocol } else { - ParseQuery(value, 0, value.Length, true); + ParseQuery(value, 0, value.Length, true, false); } } } @@ -1109,7 +1109,7 @@ private Boolean ParsePath(String input, Int32 index, Int32 length, Boolean onlyP return true; } - private Boolean ParseQuery(String input, Int32 index, Int32 length, Boolean onlyQuery = false) + internal Boolean ParseQuery(String input, Int32 index, Int32 length, Boolean onlyQuery = false, Boolean fromParams = false) { var buffer = StringBuilderPool.Obtain(); var fragment = false; @@ -1137,7 +1137,12 @@ private Boolean ParseQuery(String input, Int32 index, Int32 length, Boolean only } _query = buffer.ToPool(); - _params?.ChangeTo(_query); + + if (!fromParams) + { + _params?.ChangeTo(_query, true); + } + return fragment ? ParseFragment(input, index + 1, length) : true; } diff --git a/src/AngleSharp/Dom/UrlSearchParams.cs b/src/AngleSharp/Dom/UrlSearchParams.cs index 00fccf152..d9925dcc6 100644 --- a/src/AngleSharp/Dom/UrlSearchParams.cs +++ b/src/AngleSharp/Dom/UrlSearchParams.cs @@ -17,14 +17,7 @@ public class UrlSearchParams #region Fields private readonly List> _values; - private Boolean _reflow; - - #endregion - - #region Events - - internal event Action? Changed; - + private readonly Url? _parent; #endregion #region Constructors @@ -35,18 +28,16 @@ public class UrlSearchParams [DomConstructor] public UrlSearchParams() => _values = new(); - internal UrlSearchParams(Url parent) : this(parent.Query ?? String.Empty) => Changed += qs => + internal UrlSearchParams(Url parent) : this(parent.Query ?? String.Empty) { - _reflow = true; - parent.Query = qs; - _reflow = false; - }; + _parent = parent; + } /// /// Creates a new instance filled from the provided string. /// [DomConstructor] - public UrlSearchParams(String init) : this() => ChangeTo(init); + public UrlSearchParams(String init) : this() => ChangeTo(init, false); #endregion @@ -54,31 +45,30 @@ public class UrlSearchParams internal void Reset() => _values.Clear(); - internal void ChangeTo(String query) + internal void ChangeTo(String query, Boolean fromParent) { - if (!_reflow) + Reset(); + + if (query is "") + { + return; + } + + foreach (var pair in query.Split('&')) { - Reset(); + var kvp = pair.Split('='); - if (query is "") + if (kvp.Length > 1) { - return; + AppendCore(Decode(kvp[0]), Decode(kvp[1])); } - - foreach (var pair in query.Split('&')) + else { - var kvp = pair.Split('='); - - if (kvp.Length > 1) - { - Append(Decode(kvp[0]), Decode(kvp[1])); - } - else - { - Append(Decode(pair), String.Empty); - } + AppendCore(Decode(pair), String.Empty); } } + + RaiseChanged(fromParent); } /// @@ -88,9 +78,14 @@ internal void ChangeTo(String query) /// The value of the param. [DomName("append")] public void Append(String name, String value) + { + AppendCore(name, value); + RaiseChanged(false); + } + + private void AppendCore(String name, String value) { _values.Add(new KeyValuePair(name, value)); - RaiseChanged(); } /// @@ -99,9 +94,14 @@ public void Append(String name, String value) /// The name of the param. [DomName("delete")] public void Delete(String name) + { + DeleteCore(name); + RaiseChanged(false); + } + + private void DeleteCore(String name) { _values.RemoveAll(p => p.Key == name); - RaiseChanged(); } /// @@ -139,15 +139,15 @@ public void Set(String name, String value) if (Has(name)) { var index = _values.FindIndex(p => p.Key == name); - Delete(name); + DeleteCore(name); _values.Insert(index, new KeyValuePair(name, value)); } else { - Append(name, value); + AppendCore(name, value); } - RaiseChanged(); + RaiseChanged(false); } /// @@ -157,7 +157,7 @@ public void Set(String name, String value) public void Sort() { _values.Sort((a, b) => a.Key.CompareTo(b.Key)); - RaiseChanged(); + RaiseChanged(false); } /// @@ -210,7 +210,14 @@ private static String Decode(String value) return sb.ToPool(); } - private void RaiseChanged() => Changed?.Invoke(ToString()); + private void RaiseChanged(Boolean fromParent) + { + if (!fromParent) + { + var qs = ToString(); + _parent?.ParseQuery(qs, 0, qs.Length, true, true); + } + } #endregion }