Implement ALTERNATE-SERVER for TURN
Categories
(Core :: WebRTC: Networking, defect, P2)
Tracking
()
Tracking | Status | |
---|---|---|
firefox90 | --- | fixed |
backlog | webrtc/webaudio+ |
People
(Reporter: ekr, Assigned: bwc)
References
(Blocks 1 open bug)
Details
(Whiteboard: [turn][fuzzing:queue:cdiehl])
Attachments
(20 files, 2 obsolete files)
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review | |
48 bytes,
text/x-phabricator-request
|
Details | Review |
No description provided.
Updated•11 years ago
|
Updated•11 years ago
|
Comment 1•10 years ago
|
||
I am currently working on implementing this, could someone assign the bug to me?
Reporter | ||
Updated•10 years ago
|
Comment 2•10 years ago
|
||
rohan: thanks! make sure you tell hg who you are when you upload a patch, and once you have something to try we can help guide you through the process of testing, reviewing and landing a patch.
Comment 3•10 years ago
|
||
I ran "./mach mercurial-setup" which should have set the correct hg variables right? As for the patch itself, I have it working up to the point where the request is redirected to the alternate server and the alternate server replies with a stale nonce error code. I just need to figure out why the code doesn't send another request with the new nonce specified in the error response.
Comment 4•10 years ago
|
||
(In reply to Rohan Garg from comment #3) > I ran "./mach mercurial-setup" which should have set the correct hg > variables right? I'm not familiar with that command. The manual way to do this is go into ~/.hgrc and add something like: [ui] username = Rohan Garg <rohan16garg@gmail.com> > As for the patch itself, I have it working up to the point where the request > is redirected to the alternate server and the alternate server replies with > a stale nonce error code. I just need to figure out why the code doesn't > send another request with the new nonce specified in the error response. Go ahead and upload your patch with a description starting with "WIP" (for "Work In Progress"), and then needinfo me (check the "Need more information from" box). I can't guarantee a fast response (this week is the WebRTC interim standards meeting, and then I'm travelling for the Memorial Day weekend), but I'll try to give it a look and see what's going on when I do get some time.
Comment 5•10 years ago
|
||
I have my work on github in this [1] branch. Here's where I'm stuck : The socket that is used to communicate with the TURN server is owned by the test. However, when the negotiation ends in a ALTERNATE-SERVER state for TCP connections, a new socket is required since the same socket cannot be reused for the new TCP connection. What I propose is this : Modify the nr_turn_client_allocate [2] function to have a error_cb callback that can handle errors. Once the restart function detects that it has to try an alternate server and that it's operating in TCP mode, we cancel the current transaction and close the socket. We then call the error call back. In the error callback we check if the turn_client_ctx state is NR_TURN_CLIENT_STATE_CANCELLED and whether the turn_client_ctx has a alternate turn server attribute, if so, we allocate a new socket and start the whole thing from scratch. How does that sound? :) [1] https://github.com/shadeslayer/gecko-dev/tree/fix-for-857668 [2] http://dxr.mozilla.org/mozilla-central/source/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c?from=turn_client_ctx.c#626
Comment 6•10 years ago
|
||
Oh, right, cons of above approach, breaks API/ABI, but I'm not sure how API/ABI breaks are handled in internal libraries in Firefox.
Comment 7•10 years ago
|
||
Comment 8•10 years ago
|
||
Byron: I'm at the WebRTC interim this week -- can you give this a look?
Assignee | ||
Comment 9•10 years ago
|
||
Comment on attachment 8425039 [details] [diff] [review] WIP: Make nICEr handle the ALTERNATE SERVER state Review of attachment 8425039 [details] [diff] [review]: ----------------------------------------------------------------- I think you're going to want to teach everything in the chain how to reconnect/restart, since replacing internal members is going to have lifecycle implications (I've called out some that I spotted, there may be others). Pointers to these socket objects are scattered all over the place, and getting them all is going to be time-consuming and error-prone. Another alternative would be to create a brand new candidate with all new sockets and such; a clean start. You would have to watch out for redirect loops though. ::: media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c @@ +164,5 @@ > } > > + if (ctx->my_addr.protocol == IPPROTO_TCP) { > + ctx->peer_addr.protocol=IPPROTO_TCP; > + } You'll need to also call nr_transport_addr_fmt_addr_string here. @@ +169,2 @@ > nr_stun_client_reset(ctx); > + ctx->error_code = 0; So, between the nr_stun_client_reset and setting the error code to 0, there's a whole bunch of stuff left. The label doesn't seem to change (and might be misleading now), and none of the auth stuff seems to change (not sure how much of this we should change, would have to sit and think about it). ::: media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c @@ +231,5 @@ > + if (tctx->turn_server_addr.protocol == IPPROTO_TCP) { > + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Closing TCP socket for restart", ctx->tctx->label); > + nr_socket_close(tctx->sock); > + > + r = nr_ip4_port_to_transport_addr(0, 0, IPPROTO_TCP, &addr); Error check? @@ +232,5 @@ > + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Closing TCP socket for restart", ctx->tctx->label); > + nr_socket_close(tctx->sock); > + > + r = nr_ip4_port_to_transport_addr(0, 0, IPPROTO_TCP, &addr); > + r = nr_socket_local_create(&addr, &real_socket_); We're probably going to want to use the same local address we were using before. Also, error check? @@ +233,5 @@ > + nr_socket_close(tctx->sock); > + > + r = nr_ip4_port_to_transport_addr(0, 0, IPPROTO_TCP, &addr); > + r = nr_socket_local_create(&addr, &real_socket_); > + nr_socket_buffered_stun_create(real_socket_, 100000, The only other call (excluding test code) I see to nr_socket_buffered_stun_create uses NR_STUN_MAX_MESSAGE_SIZE, and not 100000. Also, we need to do error handling here, and clean stuff up if we hit a failure (in this case, real_socket_). @@ +237,5 @@ > + nr_socket_buffered_stun_create(real_socket_, 100000, > + &buffered_socket_); > + > + tctx->sock = buffered_socket_; > + ctx->stun->sock = buffered_socket_; I'm pretty sure there's an nr_ice_socket around with an old buffered socket pointer in it, see here: http://dxr.mozilla.org/mozilla-central/source/media/mtransport/third_party/nICEr/src/ice/ice_component.c#335 Also, the nr_ice_socket is hooked up to a signal based on the fd of the old buffered socket: http://dxr.mozilla.org/mozilla-central/source/media/mtransport/third_party/nICEr/src/ice/ice_socket.c#221 Maybe the thing to do is build reconnect/restart logic into the nr_socket API, so we don't have to worry about lifecycle as much? @@ +243,5 @@ > + if (nr_stun_message_has_attribute(ctx->stun->response, NR_STUN_ATTR_ERROR_CODE, &ec) > + && ec->u.error_code.number == 300) { > + if (nr_stun_message_has_attribute(ctx->stun->response, NR_STUN_ATTR_ALTERNATE_SERVER, &as)) { > + nr_transport_addr_copy(&tctx->turn_server_addr, &as->u.alternate_server); > + tctx->turn_server_addr.protocol=IPPROTO_TCP; Don't forget to call nr_transport_addr_fmt_addr_string here. @@ +246,5 @@ > + nr_transport_addr_copy(&tctx->turn_server_addr, &as->u.alternate_server); > + tctx->turn_server_addr.protocol=IPPROTO_TCP; > + } > + } > + nr_turn_client_connect(tctx); Probably need to do error handling here, but ensure that R_WOULDBLOCK is not treated as a failure. (I imagine what you want to end up doing here is creating an nr_turn_client_restart/reconnect?) @@ +325,5 @@ > > ctx->retry_ct++; > + } else if (ctx->stun->error_code == 300) { > + r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): Alternate server recieved, restarting TURN", ctx->tctx->label); > + ctx->retry_ct = 0; Hmm. What if we get into a redirect loop?
Assignee | ||
Updated•10 years ago
|
Comment 10•10 years ago
|
||
Since the socket is managed by applications consuming nICEr , I propose that nr_turn_client_allocate be modified so that it can call a error callback. This error callback is a function in the application consuming nICEr and can check the error code of the TURN process and if a 300 error code is present, it proceeds to restart the process with a new socket. Does this approach seem sensible? Cheers
Comment 11•10 years ago
|
||
Sorry this took so long -- I'm a bit hesitant to make an architectural change that is so visible to the using application without first getting Eric's input, as he was responsible for the original design.
Reporter | ||
Comment 12•10 years ago
|
||
(In reply to Rohan Garg from comment #10) > Since the socket is managed by applications consuming nICEr , I propose that > nr_turn_client_allocate be modified so that it can call a error callback. > > This error callback is a function in the application consuming nICEr and can > check the error code of the TURN process and if a 300 error code is present, > it proceeds to restart the process with a new socket. > > Does this approach seem sensible? If I understand your question, no, I don't think that's right. The applications consuming nICEr never see *any* socket. If you mean that the ICE part of nICEr needs to handle a failure of the TURN part, that might be OK.
Comment 13•9 years ago
|
||
Just curious if there has been any progress on this. I'm currently architecting our TURN server environment and I'd really like to see support for ALTERNATE_SERVER 300 in firefox. Is there any new information?
Updated•9 years ago
|
Comment 14•8 years ago
|
||
I would really like to see support for ALTERNATE-SERVER in Firefox!!!
Updated•8 years ago
|
Comment 15•7 years ago
|
||
Any news on this? Chromium implemented it in 2015 (https://bugs.chromium.org/p/webrtc/issues/detail?id=1986), Firefox is the missing piece to enable easier load balancing of TURN servers... Would be *awesome* if this would be implemented :)
Comment 16•7 years ago
|
||
Mass change P2->P3 to align with new Mozilla triage process.
Comment 17•5 years ago
|
||
Since rohan16garg@gmail.com is not working on this any more I am removing the NI.
Comment 18•3 years ago
|
||
Hi Team, Any News on this? Is it implented in firefox?
Updated•3 years ago
|
Comment 19•3 years ago
|
||
I tried a two different approaches to solving this, which involved directly re-using the stun context and some higher up. But I have come to the conclusion that bwc's review from comment #9 is still holds true.
It looks like the most sane way approaching this feature is to add some functions which can replicate a TURN server context, without the sockets in it. Then set or replace the IP address of the target TURN server in it that cloned context. And then feed this TURN server high up in the nICEr logic, and ignore the original turn context as failed.
That way we might also be able to build in loop detection, by checking for already failed TURN contexts with the same IP address etc in it.
Assignee | ||
Comment 20•3 years ago
|
||
So first, we have to figure out how we're going to test this. Because we don't necessarily have multi-homed hosts in CI, and do not have a test STUN/TURN server on the internet that we administer (and could configure to do redirects), I think the most viable option is to have some wrapper code (eg; in the NAT simulator) that will send redirect responses when the destination addr/port matches a specific value (configured with a pref, I guess).
Assignee | ||
Comment 21•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=2ca11cf94b1a1fcca83a238c46c924c1a0e21ddd
Assignee | ||
Comment 22•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=5ed269df397732d4d166b515db3eb39deb12b7ba
Assignee | ||
Comment 23•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=bc3c9ec4d7d938a335274a92a5e05dbaeac292a2
Assignee | ||
Comment 24•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=ee216f9e83080f317a0dca189a146898401b4384
Assignee | ||
Comment 25•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=47b86eb9854badb3a9245b81425c4d26c70b9f93
Assignee | ||
Comment 26•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=be729b23f5548350250f1d86d884608fb31835b6
Assignee | ||
Comment 27•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=0f38aa661cf94dfa054703776d92d5bdab7463ab
Assignee | ||
Comment 28•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=8c83e648f368d043d20bb36a236255c064e6b07b
Assignee | ||
Comment 29•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=268f6cd76028ecea4d2feb9c55e986c60a345ae4
Assignee | ||
Comment 30•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=68f7f59829c7249d7bc035ff77547d75152b6c85
Assignee | ||
Comment 31•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=e65e96bdab2d1a81c26052b38309ee0e8d550df3
Assignee | ||
Comment 32•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=a20669e072d65d227511d845cedd89b6291dd163
Assignee | ||
Comment 33•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=f740d9d7653064e13695c59d143213401c754431
Assignee | ||
Comment 34•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=a8ad1e6626b055e332fb450b961091109f333361
Assignee | ||
Comment 35•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=bc2b2c5bfd7f8629ac21a70f8f0ba4525d970218
Assignee | ||
Comment 36•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=46866b15814f6dd17365e4e39f42dfd7f080f74a
Assignee | ||
Comment 37•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=4f2f77fa928ca14167bc2ac9c39af00c0cf5ff53
Assignee | ||
Comment 38•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=1a4d8cb08f839b79beef026efa61318f072edbc3
Assignee | ||
Comment 39•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=7cfb78b33082d421bb2696b92b58854716ff3874
Assignee | ||
Comment 40•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=93ba50a582110feeec3d1a5fc7aa454f5897ed9e
Assignee | ||
Comment 41•3 years ago
|
||
Assignee | ||
Comment 42•3 years ago
|
||
Depends on D115275
Assignee | ||
Comment 43•3 years ago
|
||
We need this because the test TURN server does not support stun redirects where
the address changes. Even if we were to add this support to the test STUN
server, it would not be helpful in CI since it would require having additional
IP addresses, which we cannot count on since we do not control the network
configuration of those machines.
Also, the fact that the code in this patch does not support MESSAGE-INTEGRITY
allows us to test the case where it is not present in a STUN redirect. Some
deployments rely on this.
Lastly, this code supports adding multiple (or 0) ALTERNATE-SERVER fields,
which is something that the test TURN server does not support.
Depends on D115276
Assignee | ||
Comment 44•3 years ago
|
||
Depends on D115277
Assignee | ||
Comment 45•3 years ago
|
||
Depends on D115278
Assignee | ||
Comment 46•3 years ago
|
||
Depends on D115279
Assignee | ||
Comment 47•3 years ago
|
||
This substantially simplifies the task of replacing the nr_socket a TURN
context is using.
Depends on D115280
Assignee | ||
Comment 48•3 years ago
|
||
Depends on D115282
Assignee | ||
Comment 49•3 years ago
|
||
Depends on D115283
Assignee | ||
Comment 50•3 years ago
|
||
STUN/300 is only meaningful for specific STUN method types, and the handling
depends on the spec that those methods are defined in. Doing any handling in
the base stun client code is not appropriate.
Depends on D115284
Assignee | ||
Comment 51•3 years ago
|
||
Depends on D115285
Assignee | ||
Comment 52•3 years ago
|
||
Also some logging that was useful in debugging this.
Depends on D115286
Assignee | ||
Comment 53•3 years ago
|
||
Leaking test-case was dom/media/webrtc/tests/mochitests/test_peerConnection_bug825703.html
Depends on D115287
Assignee | ||
Comment 54•3 years ago
|
||
Depends on D115288
Assignee | ||
Comment 55•3 years ago
|
||
Also some indentation fixup.
Depends on D115289
Assignee | ||
Comment 56•3 years ago
|
||
Depends on D115290
Assignee | ||
Comment 57•3 years ago
|
||
Depends on D115291
Assignee | ||
Comment 58•3 years ago
|
||
Nothing used this function before this bug, so this field has never been used
anyway. We handle challenge loops with nr_turn_stun_ctx.retry_ct.
Depends on D115292
Assignee | ||
Updated•3 years ago
|
Assignee | ||
Comment 59•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=f7989f3589a47f2c7254244dcdefcad7b68e4170
Assignee | ||
Comment 60•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=5f5ef81170663d08c6bd1a5a5251ecebfc0be3cf
Assignee | ||
Comment 61•3 years ago
|
||
We only use this with TCP sockets.
Depends on D115293
Assignee | ||
Comment 62•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=aef65c54fbda5faf6901a1ce8276be97b7e886b5
Assignee | ||
Comment 63•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=29b37a3a6238396c7ecb0a6bb2ed3eccf946257d
Assignee | ||
Comment 64•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=317a88644c245bf2efb4df64e2c646e0c3017839
Assignee | ||
Comment 65•3 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=6276ed3d607f8bf31b05e4c261d13c49eb02a69f
Assignee | ||
Comment 66•3 years ago
•
|
||
Try looks ok at this point. https://treeherder.mozilla.org/#/jobs?repo=try&revision=92766130a9deb8b34cc8949902752970f6f176c3 https://treeherder.mozilla.org/jobs?repo=try&revision=9510f7bf4f4824b911d3547f84e73901195d2172 https://treeherder.mozilla.org/jobs?repo=try&revision=a3b188fb0ca15ad8ea1b696fb6435801ecb8f8d3 https://treeherder.mozilla.org/jobs?repo=try&revision=c102cf4b012c4faf9159ef8731da8de77025a73b
Assignee | ||
Comment 67•3 years ago
|
||
Depends on D115829
Assignee | ||
Comment 68•3 years ago
|
||
This avoids shutdown leaks in cases where these tests are run in isolation.
Depends on D116029
Updated•3 years ago
|
Comment 69•3 years ago
|
||
Pushed by bcampen@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/143bfb916b26 Some IPv6 fixes and refactoring in the test TURN server. r=mjf,ahal https://hg.mozilla.org/integration/autoland/rev/2435ef0cd258 Teach test TURN server to open additional ports, on which it will respond with 300. r=mjf,ahal https://hg.mozilla.org/integration/autoland/rev/08808a003231 Teach NAT simulator to do STUN redirects, configured by prefs. r=mjf https://hg.mozilla.org/integration/autoland/rev/edaaced4f42b Work a little harder to make sure we start tests with a reasonable pref environment. r=jib https://hg.mozilla.org/integration/autoland/rev/76f4a828537e Test cases for bug. r=jib https://hg.mozilla.org/integration/autoland/rev/8aaf855abaed Remove some unneeded non-owning references to nr_socket. r=mjf https://hg.mozilla.org/integration/autoland/rev/9dc0836dcc9b Remove some fields that serve no purpose besides making this code harder to maintain. r=mjf https://hg.mozilla.org/integration/autoland/rev/86b7a16b487f Add function for getting STUN multi-attributes. r=mjf https://hg.mozilla.org/integration/autoland/rev/286b02c1eb23 Remove existing STUN/300 handling. r=mjf https://hg.mozilla.org/integration/autoland/rev/2adc8298e2fa Handle Allocate/300 with ALTERNATE-SERVER. r=mjf https://hg.mozilla.org/integration/autoland/rev/97a666f07a57 Fix handling of IPv6 literals. r=mjf https://hg.mozilla.org/integration/autoland/rev/5845d6f99b76 Fix a leak that the previous patch uncovered. r=mjf https://hg.mozilla.org/integration/autoland/rev/ea6463ca6695 Let Allocate/300 without MESSAGE-INTEGRITY slide. r=mjf https://hg.mozilla.org/integration/autoland/rev/4c540c962a19 Stop ignoring error responses from STUN/TURN servers. r=mjf https://hg.mozilla.org/integration/autoland/rev/b8158a2544e1 Open a new TCP socket when we receive a redirect in the TCP case. r=mjf https://hg.mozilla.org/integration/autoland/rev/c858c4bbd547 Implement redirect loop detection. r=mjf https://hg.mozilla.org/integration/autoland/rev/c290554ad7e4 Remove this extra retry_ct field. r=mjf https://hg.mozilla.org/integration/autoland/rev/798446339428 Use IPPROTO_TCP here. r=mjf https://hg.mozilla.org/integration/autoland/rev/96849c0ceebe Remove this check; sockets are sometimes closed without nICEr knowing about it. r=mjf https://hg.mozilla.org/integration/autoland/rev/e643486292f0 Close these RTCPeerConnections. r=jib
Comment 70•3 years ago
|
||
bugherder |
https://hg.mozilla.org/mozilla-central/rev/143bfb916b26
https://hg.mozilla.org/mozilla-central/rev/2435ef0cd258
https://hg.mozilla.org/mozilla-central/rev/08808a003231
https://hg.mozilla.org/mozilla-central/rev/edaaced4f42b
https://hg.mozilla.org/mozilla-central/rev/76f4a828537e
https://hg.mozilla.org/mozilla-central/rev/8aaf855abaed
https://hg.mozilla.org/mozilla-central/rev/9dc0836dcc9b
https://hg.mozilla.org/mozilla-central/rev/86b7a16b487f
https://hg.mozilla.org/mozilla-central/rev/286b02c1eb23
https://hg.mozilla.org/mozilla-central/rev/2adc8298e2fa
https://hg.mozilla.org/mozilla-central/rev/97a666f07a57
https://hg.mozilla.org/mozilla-central/rev/5845d6f99b76
https://hg.mozilla.org/mozilla-central/rev/ea6463ca6695
https://hg.mozilla.org/mozilla-central/rev/4c540c962a19
https://hg.mozilla.org/mozilla-central/rev/b8158a2544e1
https://hg.mozilla.org/mozilla-central/rev/c858c4bbd547
https://hg.mozilla.org/mozilla-central/rev/c290554ad7e4
https://hg.mozilla.org/mozilla-central/rev/798446339428
https://hg.mozilla.org/mozilla-central/rev/96849c0ceebe
https://hg.mozilla.org/mozilla-central/rev/e643486292f0
Description
•