This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 24367 - Behaviour of URLUtils.searchParams setter
Summary: Behaviour of URLUtils.searchParams setter
Status: RESOLVED FIXED
Alias: None
Product: WHATWG
Classification: Unclassified
Component: URL (show other bugs)
Version: unspecified
Hardware: PC Windows NT
: P2 normal
Target Milestone: Unsorted
Assignee: Anne
QA Contact: sideshowbarker+urlspec
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-01-22 20:57 UTC by sof
Modified: 2014-03-19 13:55 UTC (History)
3 users (show)

See Also:


Attachments

Description sof 2014-01-22 20:57:55 UTC
The spec currently requires that upon setting URLUtils.searchParams with a URLSearchParams object with an existing "url object" association, it should create a copy of that object,

 http://url.spec.whatwg.org/#dom-url-searchparams

Hence,

  var url = new URL("...");
  var us = new URLSearchParams("a=b");

  // url object is not set for "us", assign it as "url"'s query object.
  url.searchParams = us;

  us.set("a", "c");
  // us.set() observable.
  assert_equals(url.search, "?a=c");

  // url object is set for "us", copy it (and clear "us"' url object connection.)
  url.searchParams = us;

  us.set("a", "d");
  // us.set() not observable.
  assert_equals(url.search, "?a=c");

  // url object not set for "us", assign it as "url"'s query object.
  url.searchParams = us;
  us.set("a", "e");
  // us.set() observable (again.)
  assert_equals(url.search, "?a=e");

Should the semantics of re-setting the query object via URLUtils.searchParams be different, or is this intended?
Comment 1 Erik Arvidsson 2014-01-24 16:39:20 UTC
Sigbjørn, I find the current behavior a bit confusing.

I think we should just create a copy on assign. It just makes the semantics clear. And I believe that is what the spec says?

This is the behavior I want.

var url = new URL("http://example.com/?a=b");
var us = url.searchParams;
us.set("a", "c");
assert_equals(url.search, "?a=c");

url.searchParams = us; // us is now disconnected from url
us.set("a", "d");
assert_equals(url.search, "?a=c");
assert_equals(us.toString(), "a=d");

If we do not disconnect it we get into strange cases when it is assigned to multiple urls.

(Firefox seems to ignore the set if the searchParams is set to the existing searchParams.)
Comment 2 sof 2014-01-24 16:50:08 UTC
The spec does not allow copying if the "url object" of the URLSearchParams isn't set (step 3 and 4),

  http://url.spec.whatwg.org/#dom-url-searchparams

It could either always copy, or only copy in step 3 if the assigned object differs from the current one.
Comment 3 sof 2014-01-24 17:05:32 UTC
FWIW, I'm fine with either :) (including not having URLSearchParams as something separately constructable.)
Comment 4 Anne 2014-01-24 18:00:09 UTC
Mostly copying makes sense to me. Should we special case when you assign the same object? I.e. when you do url.searchParams = url.searchParams? Not copying in that case would make sense to me.
Comment 5 Erik Arvidsson 2014-01-24 18:06:08 UTC
I prefer not special casing. It leads to more things to remember and you cannot look at the code to see what is going on.

function f(url, params) {
  url.searchParams = params;
  params.set('a', 'b'); // mutates url or not?
}
Comment 6 Anne 2014-01-24 18:17:29 UTC
Fair.

baku suggested on IRC to let a URLSearchParams be associated with many URL objects. As that seemed closer to JavaScript. The problem with that is that then you need to update many URL objects if your URLSearchParams changes (and maybe they need to do that in order if it becomes observable).
Comment 7 sof 2014-01-24 18:26:45 UTC
arv's example convinced me; simple & functional.
Comment 8 Anne 2014-01-24 19:19:23 UTC
Domenic also argued for what baku suggested. The reason is that then you preserve

  x.y = z
  x.y === z


That would mean that whenever we assign a URLSearchParams to a URL, we'd do something like this:

1. If URL already has an associated URLSearchParams, remove URL from that URLSearchParams.

2. Add URL to the new URLSearchParams

And whenever a URLSearchParams is updated we iterate through its URLs and update each in turn.
Comment 9 Anne 2014-01-24 19:19:58 UTC
The example in comment 5 would then always result in mutating.
Comment 10 Erik Arvidsson 2014-01-24 20:57:33 UTC
(In reply to Anne from comment #8)
> And whenever a URLSearchParams is updated we iterate through its URLs and
> update each in turn.

That is how most JS implementations would work. I'm fine with that too even though it is a bit more complex to implement on the C++ side.
Comment 11 Anne 2014-01-24 21:26:40 UTC
Ok. I will make it like that then. I might not get this fixed for a few weeks so please proceed assuming comment 8 is part of the standard.
Comment 12 sof 2014-01-24 21:31:33 UTC
I'm struggling to see the need for such generality.

One way to sidestep all of this is to make url.searchParams a readonly attribute, and remove all the URLParams constructors. You can then (only) use url.searchParams to manipulate the query portion of one URL.

If you want to copy the query portion from one URLUtils-implementing object to another, use url.search.
Comment 13 Anne 2014-01-24 21:40:11 UTC
If we offer url.searchParams.replaceWith(new URLSearchParams(...)) or some such that might be okay too. Hmm...
Comment 14 Erik Arvidsson 2014-01-24 21:43:14 UTC
(In reply to Anne from comment #13)
> If we offer url.searchParams.replaceWith(new URLSearchParams(...)) or some
> such that might be okay too. Hmm...

Ugh.

Here are the options

1. Copy on assign
2. Keep references to multiple owners
3. Read only (you can always use .search)

in the order I prefer.
Comment 15 Domenic Denicola 2014-01-24 22:48:02 UTC
I think copy on assign is kind of wonky, as it doesn't match normal semantics of assigning to a property. (I.e., those you expect from a data property.) Yes, the web platform has (ab)used setters to make assignment mean some pretty different things in the past, but I personally would prefer to keep the semantics as much as possible.

That's why "Keep references to multiple owners" seems best.

After that I think non-writable searchParams + `url.searchParams.replaceWith(new URLSearchParams(...))` is pretty decent. But if Arv doesn't like it, then I think the liked-or-at-least-tolerated-by-everyone option is indeed "Keep references to multiple owners."
Comment 17 sof 2014-01-31 22:07:15 UTC
That was quick! One potential user pitfall here is aliasing vs copying, i.e.,

 url2.searchParams = url1.searchParams;

vs

 url2.searchParams = new URLSearchParams(url1.searchParams);

I still don't understand the use for 1-N associations, but ok.
Comment 18 sof 2014-03-13 21:54:47 UTC
According to the current spec text, the following holds: 

  var usp = new URLSearchParams();
  url.searchParams = usp;
  url.searchParams = null;
  usp.set("a", "b");
  assert_true("?a=b", url.searchParams.search);

i.e., "url.searchParams = null;" has no effect, unlike "url.search = '';" (or "url.searchParams = new URLSearchParams();")

As intended, or should the setter steps for http://url.spec.whatwg.org/#dom-url-searchparams be re-ordered a bit for the null case?
Comment 19 sof 2014-03-14 07:04:50 UTC
(In reply to sof from comment #18)
> According to the current spec text, the following holds: 
> 
..
>   assert_true("?a=b", url.searchParams.search);
> 

Typo, should have been: assert_true("?a=b", url.search);
Comment 20 Anne 2014-03-18 11:30:08 UTC
So what effect do you think it should have? If we had different signatures for set and get I would allow returning null but not setting to null. So maybe it should throw a TypeError instead?
Comment 21 sof 2014-03-18 16:05:50 UTC
I guess I need to understand why the attribute is nullable to start with to answer that. Could you explain, please? :)
Comment 22 Anne 2014-03-18 19:19:50 UTC
I think it was nullable because at one point not all URLs had a query. That changed, so we should change the API too (to not be nullable).
Comment 24 sof 2014-03-19 13:55:35 UTC
That takes care of it from here; thank you.