Re: Intent to Ship: User Activation v2

413 views
Skip to first unread message

Mustaq Ahmed

unread,
Jul 27, 2018, 11:17:10 AM7/27/18
to blink-dev, Rick Byers, Ojan Vafai, Domenic Denicola

Contact emails

mus...@chromium.org


Explainer

https://mustaqahmed.github.io/user-activation-v2/


Spec


Summary

Browsers control access to "abusable" of APIs (e.g. opening popups or vibrating) through user activation (or "user gestures").  This lacks a reasonable spec, and major browsers today exhibit widely divergent behavior.


User Activation v2 (UAv2) defines a behavior that is simple enough for cross-browser implementation while supporting all major use cases.  In this model, user activation states will be maintained in frames, replacing Chromium’s current stack-based tokens that activation-aware APIs are expected to store/forward as needed.


Link to “Intent to Implement” blink-dev discussion

https://groups.google.com/a/chromium.org/d/msg/blink-dev/bJ2icZEANV4/5VlT_RKrBgAJ


Link to Origin Trial feedback summary

We are currently running a trial on M69 Canary/Dev.


Is this feature supported on all six Blink platforms (Windows, Mac, Linux, Chrome OS, Android, and Android WebView)?

Yes.


Demo link

Shows user activation visibility across the frame tree through popups (needs chrome://flags/#user-activation-v2): https://mustaqahmed.github.io/user-activation-v2/propagation/


Debuggability

UAv2 won’t require DevTools changes: it can be tested through activation-aware APIs (e.g. fullscreen and vibration) in the same way we do today with existing implementations.


However, UAv2 introduces a subtle difference in Chromium when the testing involves interleaved user interaction between the web page and DevTools.  One challenge we have with the existing implementation is that is that interactions with DevTools (e.g. typing, clicking) clobber page’s activation state.  UAv2’s removal of stack-allocated tokens widens the scope of this clobbering, but only for transient activation state. It’s a narrow corner case, so we don’t think it’s something developers would notice.  If this turns out otherwise, we can turn off UAv2 notifications from DevTools to renderer/browser (could be through an option).


Risks

Interoperability and Compatibility

User activation behavior is currently inconsistent among the major browsers in many different ways (e.g. for popups and with triggering events), so the interop question is moot here.  UAv2 in fact sets a good direction for future interop: the simplicity of the model makes it easier for the Web to possibly converge in future.


Compat risk should be low because the token-less model of UAv2 would mostly appear as filling incompleteness in current behavior (e.g. with Promise, postMessages, XHR, setTimeout etc).  Sources of possible risks are as follows:

  1. User activation visibility changes from renderer-wide to frame-scoped, which won’t work if a child frame tries to access parent’s activation.  We haven’t seen any real breakage like this. More importantly, the current renderer-wide visibility model is already broken with site-isolation (except where we added a hack, like this).

  2. We have some risk around popup blocking because of complexities around user activation state during navigations.  The cases we have seen so far (e.g. crbug.com/807609 and a very recent one: crbug.com/867599) are implementation cracks rather than model problems, hence fixable.


Edge: No public signals.

Firefox: Mixed signals (publicly reviewed the design, raised a single concern so far).

Safari: No signals.

Web developers: Positive (e.g. appreciated on a popular bug, suggested a tweak).


Ergonomics

UAv2 won’t cause any performance issue.


Activation

  • Nothing should change from developers’ perspective since they don’t use user activation directly (instead indirectly rely on it through activation-aware APIs).

  • A polyfill is not applicable: browser shouldn’t let webpages mimic user activation.


Is this feature fully tested by web-platform-tests?

We have partial coverage for UAv2 in wpt today:


Entry on the feature dashboard

https://www.chromestatus.com/features/5722065667620864


Boris Zbarsky

unread,
Jul 27, 2018, 12:05:48 PM7/27/18
to Mustaq Ahmed, blink-dev, Rick Byers, Ojan Vafai, Domenic Denicola
On 7/27/18 11:16 AM, Mustaq Ahmed wrote:
> Firefox: Mixed signals (publicly reviewed the design
> <https://github.com/whatwg/html/issues/1903>, raised a single concern
> <https://github.com/whatwg/html/issues/1903#issuecomment-324968512>so far).

https://github.com/whatwg/html/issues/1903#issuecomment-405339230 is
there too, though maybe it's conceptually the same concern? It's a more
general phrasing of the specific problem the fullscreen API's
interaction with activation has. Basically, it feels like there already
isn't a one-size-fits-all notion of "activation" that is appropriate for
all uses, and whatever work happens here should be taking that as a
baseline assumption. It's not clear to me whether that's happening.

-Boris

Mustaq Ahmed

unread,
Jul 27, 2018, 4:16:52 PM7/27/18
to bzba...@mit.edu, blink-dev, Rick Byers, Ojan Vafai, Domenic Denicola
Hi Boris:

The ability to handle API-specific differences is a valid requirement (and it's separate from the expiry problem, sorry for the confusion).  I added "classes of user-activation-aware APIs" and forked off the event-set discussion to address/defer this, at least partially.

Let me explain in detail.

This is a tradeoff question with no clear answer.  In one extreme, we have a very simple model that is easy to implement but not backward compatible.  On the other extreme, allowing too many variations in APIs could make the model so complex that no browser would risk implementing.  Even if we assume for sake of arguments that major browsers would implement a complex model, too many variation would make it harder (or even impossible) for the Web to converge to a consistent state.

Our proposed model is a middle-ground, showing a direction for the Web to converge.  Or at least a direction that seems to work for Chromium so far.  We are not ruling out future model changes, e.g. adding more states (hence more classes for user APIs) in future to address the event-set question above.

Note, however, that we want to avoid a complex solution.  "Simplicity" has been our goal from the very first draft of UAv2, to encourage cross-browser implementation.  If we are not careful, each of the ~30ish different "user" APIs we have in Chromium could end up in a "behavior class" of it own, which I am pretty sure developers won't appreciate.

Mustaq

Domenic Denicola

unread,
Jul 30, 2018, 12:53:28 PM7/30/18
to Mustaq Ahmed, bzba...@mit.edu, blink-dev, Rick Byers, Ojan Vafai, Domenic Denicola
From: Mustaq Ahmed <mus...@chromium.org>

> The ability to handle API-specific differences is a valid requirement (and it's separate from the expiry problem, sorry for the confusion).  I added "classes of user-activation-aware APIs" and forked off https://github.com/whatwg/html/issues/3849 to address/defer this, at least partially.

For the record, I also forked off https://github.com/whatwg/html/issues/3859, since I think Boris's question might have been a bit broader than just about events. But I second Mustaq's push for simplicity, and am hopeful that the proposed UAv2 spec's three types of activation gating are sufficient. Please feel free to discuss further on that issue :)

Matt Giuca

unread,
Aug 1, 2018, 3:34:33 AM8/1/18
to Domenic Denicola, mus...@chromium.org, bzba...@mit.edu, blink-dev, Rick Byers, oj...@chromium.org, dom...@chromium.org, Dominick Ng
We got a bug report that the BeforeInstallPromptEvent.prompt() method can be called repeatedly without a user gesture: https://crbug.com/869756 (btw, dominickn@ and I am responsible for this API but I've assigned to you initially for your opinion; if you just want us to change it to consume the token we can do that).

I'll dive into that below, but firstly, I think this bug shows that there are issues with UAv2 that need to be worked out before it launches. This doesn't just affect BeforeInstallPrompt, but potentially any other API that reads the user gesture token without consuming it. I think the UAv2 team needs to audit all the APIs to see where else this can go wrong. We need some advice on whether we should always consume user gesture tokens when we use them. (The advice given for BIP was not to consume the token, because it's valid to perform several user-gesture-requiring actions within the timeout. It seems this advice is no longer valid, and we should be consuming the token.)

As I said on the bug, we could change BeforeInstallPrompt.prompt() so that it consumes the user gesture token (although this was not the advice given at the time). However, that wouldn't fix what I see as the more critical design issue with UAv2. Let's assume for the rest of this analysis that we've made it consume the token.

Correct me if I'm wrong (my understanding of UAv2 is based on a discussion I had with dominickn@). It seems that under UAv1, each execution context had its own user gesture token. This means you could have something like the following sequence:
  1. User clicks a button. The click handler has a user gesture token. It fires a setTimeout(500).
  2. The beforeinstallprompt event fires. Inside that event, there is no user gesture token, so you can't show a prompt.
  3. The setTimeout completes, calling a callback. The callback has the user gesture token which was inherited from the click event handler. We can show a prompt from here.
So it's possible to have a user gesture token available to some contexts (those that were directly or indirectly triggered by a user gesture) but not others, at the same time. It seems that UAv2 is missing that nuance. There is effectively a global user gesture token that lasts until it's consumed. If there's a click event (whose user gesture is not consumed), then the user gesture flag is available to the BIP event and any other event handler that isn't triggered by a user gesture, for an unlimited amount of time. So now we have:
  1. User clicks a button. The page has a user gesture token. It fires a setTimeout(500).
  2. The beforeinstallprompt event fires. Inside that event, we have access to the global user gesture token, so we can show a prompt.
  3. The setTimeout completes, calling a callback. The callback no longer has access to the user gesture token, because it was consumed by the prompt() method.
It seems fundamentally broken that contexts that were neither directly nor indirectly triggered by a user gesture can perform user-gesture-requiring operations (and this seems to be by design in UAv2).

Now you may say "but Matt, a specially crafted site could already do this under UAv1 by carefully managing the user gesture token". Yep, something like this would work under UAv1:

    let stashed_bip = null;

    window.addEventListener('click', () => {
      setTimeout(() => {
        if (stashed_bip !== null)
          stashed_bip.prompt();
      }, 900);
    });

    window.addEventListener('beforeinstallprompt', e => {
      e.preventDefault();
      stashed_bip = e;
    });

Any click before the "beforeinstallprompt" event will wait 900ms to see if a BIP came in during that time, and if it did, it can show a prompt using the click handler's user gesture token. However, this requires deliberate effort on part of the site author. All you have to do under UAv2 to achieve the same thing is this:

    window.addEventListener('beforeinstallprompt', e => {
      e.preventDefault();
      e.prompt();
    });

Which can easily be done by accident. In fact, we have a couple of real-world sites (https://dory.withgoogle.com) that exhibit this behaviour by accident. Calling prompt() from within the "beforeinstallprompt" event handler is actually a reasonable thing to do, because other user agents may allow prompt() without a user gesture but show a non-modal dialog. Also there is no longer a timeout under UAv2. The point is that UAv2 makes it significantly easier to accidentally take advantage of a user gesture token in an event handler that was not triggered by a user gesture.

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/SN6PR05MB4878404DC6A18E83333311C7DF2F0%40SN6PR05MB4878.namprd05.prod.outlook.com.

Boris Zbarsky

unread,
Aug 3, 2018, 12:02:02 PM8/3/18
to Domenic Denicola, Mustaq Ahmed, blink-dev, Rick Byers, Ojan Vafai, Domenic Denicola
On 7/30/18 12:53 PM, Domenic Denicola wrote:
> For the record, I also forked off https://github.com/whatwg/html/issues/3859,

I will follow up there.

> and am hopeful that the proposed UAv2 spec's three types of activation gating are sufficient.

They're not, afaict. Details will be in that issue.

-Boris

Philip Jägenstedt

unread,
Aug 14, 2018, 11:12:46 AM8/14/18
to Boris Zbarsky, Domenic Denicola, Mustaq Ahmed, blink-dev, Rick Byers, Ojan Vafai, Domenic Denicola
Mustaq, are you now working through
https://github.com/whatwg/html/issues/3859, should we consider this
intent on hold?
> --
> You received this message because you are subscribed to the Google Groups "blink-dev" group.
> To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/aa1504ff-0684-4df9-a31f-08d125bb9159%40mit.edu.
>

Mustaq Ahmed

unread,
Aug 14, 2018, 1:03:47 PM8/14/18
to Matt Giuca, Philip Jägenstedt, bzba...@mit.edu, Domenic Denicola, blink-dev, Rick Byers, Ojan Vafai, Domenic Denicola
I came back from vacation this morning, just started looking into the concerns raised after Jul 31.  Please give me some time to deep dive.

Mustaq Ahmed

unread,
Aug 15, 2018, 11:19:35 AM8/15/18
to Matt Giuca, Philip Jägenstedt, bzba...@mit.edu, Domenic Denicola, blink-dev, Rick Byers, Ojan Vafai, Domenic Denicola, Dominick Ng
My answers to Matt's concerns are inline:

On Wed, Aug 1, 2018 at 3:34 AM Matt Giuca <mgi...@chromium.org> wrote:
We got a bug report that the BeforeInstallPromptEvent.prompt() method can be called repeatedly without a user gesture: https://crbug.com/869756 (btw, dominickn@ and I am responsible for this API but I've assigned to you initially for your opinion; if you just want us to change it to consume the token we can do that).

It's up to the API to decide whether it wants to consume or not.  If BIP wants to avoid repeated prompt, it should be a "transient activation consuming API" as per the proposed classification.  However, I need more details on BIP to be certain.  In particular, I don't see why hitting "Cancel" on the install prompt causes "beforeinstallprompt" event to be fired again, specially when the default action of the event seems be to show the prompt.  I will sync offline to clarify.

I'll dive into that below, but firstly, I think this bug shows that there are issues with UAv2 that need to be worked out before it launches. This doesn't just affect BeforeInstallPrompt, but potentially any other API that reads the user gesture token without consuming it. I think the UAv2 team needs to audit all the APIs to see where else this can go wrong. We need some advice on whether we should always consume user gesture tokens when we use them. (The advice given for BIP was not to consume the token, because it's valid to perform several user-gesture-requiring actions within the timeout. It seems this advice is no longer valid, and we should be consuming the token.)

I think it's still true that multiple-actions-per-activation means no consumption.  I just don't see multiple actions here---it's likely I missed something about BIP.

As for the audit question: we are tracking API specific requirements/regressions through label:UserActivation bugs.  Feel free to add the label to any relevant bugs we missed.
 
As I said on the bug, we could change BeforeInstallPrompt.prompt() so that it consumes the user gesture token (although this was not the advice given at the time). However, that wouldn't fix what I see as the more critical design issue with UAv2. Let's assume for the rest of this analysis that we've made it consume the token.

Correct me if I'm wrong (my understanding of UAv2 is based on a discussion I had with dominickn@). It seems that under UAv1, each execution context had its own user gesture token. This means you could have something like the following sequence:
  1. User clicks a button. The click handler has a user gesture token. It fires a setTimeout(500).
  2. The beforeinstallprompt event fires. Inside that event, there is no user gesture token, so you can't show a prompt.
  3. The setTimeout completes, calling a callback. The callback has the user gesture token which was inherited from the click event handler. We can show a prompt from here.
So it's possible to have a user gesture token available to some contexts (those that were directly or indirectly triggered by a user gesture) but not others, at the same time. It seems that UAv2 is missing that nuance. There is effectively a global user gesture token that lasts until it's consumed. If there's a click event (whose user gesture is not consumed), then the user gesture flag is available to the BIP event and any other event handler that isn't triggered by a user gesture, for an unlimited amount of time. So now we have:
  1. User clicks a button. The page has a user gesture token. It fires a setTimeout(500).
  2. The beforeinstallprompt event fires. Inside that event, we have access to the global user gesture token, so we can show a prompt.
  3. The setTimeout completes, calling a callback. The callback no longer has access to the user gesture token, because it was consumed by the prompt() method.
It seems fundamentally broken that contexts that were neither directly nor indirectly triggered by a user gesture can perform user-gesture-requiring operations (and this seems to be by design in UAv2).


Let me isolate two different things that contributed to the behavior you observed:

Timeout
The long timeout you observed is an implementation experiment, not a UAv2 feature (see our spec proposal).  In Chromium, we will soon bring the timeout limit down to few seconds since we are seeing arguments against it.  This will address one part of your concern.

UAv2 model change
Getting rid of per-context tokens is a change we are proposing deliberately.  The per-context gesture token idea in v1 adds unnecessary implementation complexity for APIs without a clear benefit.

First of all, token-context separation is invisible to users: if the user clicks on two different buttons before a prompt appears asynchronously, it's impossible for the user to confirm which of the two clicks triggered the prompt.  From this perspective, "fusing" consecutive clicks into one is logical.

Now consider the problems with per-context tokens:
  • Each of "user" API needs to implement its own token management/forwarding code which adds to code complexity and redundancy (e.g. this and this, sometimes multiple times for a single API).
  • Because of the complexity, popular APIs like these remain unsupported for many years.
  • The complexity resulted in many Boolean-based token handling, which can be abused to replicate user activation.
  • Correctly avoiding unchecked token replication is not easy: sometimes we had to implement API-specific hacks (like this) that sometimes stay forever (like this).
  • Even worse: complexity-induced incomplete implementation in Chromium encouraged horrible workaround for web developers (like this).
Yet, there are ways an abusive site can "leak" tokens outside its original context, as you pointed out below.

Therefore, why should we (Chromium) stick to the per-context token idea when the web lacks interop already?

Now you may say "but Matt, a specially crafted site could already do this under UAv1 by carefully managing the user gesture token". Yep, something like this would work under UAv1:

    let stashed_bip = null;

    window.addEventListener('click', () => {
      setTimeout(() => {
        if (stashed_bip !== null)
          stashed_bip.prompt();
      }, 900);
    });

    window.addEventListener('beforeinstallprompt', e => {
      e.preventDefault();
      stashed_bip = e;
    });

Any click before the "beforeinstallprompt" event will wait 900ms to see if a BIP came in during that time, and if it did, it can show a prompt using the click handler's user gesture token.
 
This only proves my point above, that per-context tokens can't guarantee unavailability of token outside the context.

However, this requires deliberate effort on part of the site author. All you have to do under UAv2 to achieve the same thing is this:

    window.addEventListener('beforeinstallprompt', e => {
      e.preventDefault();
      e.prompt();
    });

Which can easily be done by accident. In fact, we have a couple of real-world sites (https://dory.withgoogle.com) that exhibit this behaviour by accident. Calling prompt() from within the "beforeinstallprompt" event handler is actually a reasonable thing to do, because other user agents may allow prompt() without a user gesture but show a non-modal dialog. Also there is no longer a timeout under UAv2. The point is that UAv2 makes it significantly easier to accidentally take advantage of a user gesture token in an event handler that was not triggered by a user gesture.

Hopefully the "big" inline answer above explains this.

Mustaq

Mustaq Ahmed

unread,
Aug 16, 2018, 3:06:06 PM8/16/18
to Philip Jägenstedt, bzba...@mit.edu, Matt Giuca, Domenic Denicola, blink-dev, Rick Byers, Ojan Vafai, Domenic Denicola, Dominick Ng
On Tue, Aug 14, 2018 at 11:12 AM Philip Jägenstedt <foo...@chromium.org> wrote:
Mustaq, are you now working through
https://github.com/whatwg/html/issues/3859, should we consider this
intent on hold?

I would like to continue the discussion here in this intent.

As I mentioned in the spec issue above, the variations in Gecko feel inconsistent.  I wasn't surprised---I have seen too many hacks in Chromium.  IMHO, all of these is a side-effect of isolated API-specific fixes added incrementally to make user activation work in each case.

Do we really have to stick to current variations and hacks? Do we have data/evidence/analysis to show that the Web breaks apart if we don't?

I argue that the answer is "no" for both questions.


Boris Zbarsky

unread,
Aug 17, 2018, 1:55:44 PM8/17/18
to blink-dev
On 8/16/18 3:05 PM, 'Mustaq Ahmed' via blink-dev wrote:
> Do we really /have to/ stick to current variations and hacks?

No. If what you got from my comments is that that needs to happen, then
we're seriously talking past each other.

The question I am asking is whether the proposed activation model
supports the UX that browsers want to have in various cases (which is
only somewhat related to what they do right now). Answering that
question needs involvement from UX, not just engine engineers. Maybe
that's happened on the Chrome side already; it certainly hasn't happened
on the Firefox side and I would really like to get some input from them.

> Do we have data/evidence/analysis to show that the Web breaks apart if we don't?

The real question here is what makes sense for the safety and
convenience of users while also not breaking the web.

Not breaking the web at all is easy. Just have no activation-gating at
all. But no one is arguing for that, because that's not the goal here.
The goal is user experience; web breakage is just a constraint on
possible solutions.

I am on vacation until about a week and a half from now and will not be
able to follow up on this in detail until then. I will follw up in the
github issue when I get back.

-Boris

Mustaq Ahmed

unread,
Aug 24, 2018, 5:12:03 PM8/24/18
to bzba...@mit.edu, Matt Giuca, blink-dev, Philip Jägenstedt, Domenic Denicola, Rick Byers, Ojan Vafai, Domenic Denicola
Hi all:

Sorry for the delay, quite a few things happened in the meantime:

- API classification question:posted a response to Domenic's spec discussion thread, which this shows a way forward w/o changing the core model (may need changes to proposed spec wording).

- BeforeInstallPromptEvent question: We are exploring possible ways to fix BIP separately, see the bug.

- Concerns about the token-less model: To supplement my previous argument (on my Aug 15 post here) that the token-less model is better from compat/implementation perspective, I ran a quick interop experiment with popups, fullscreen and vibration.  Here is the outcome---shows specific interop issues with token passing.

- Expiry of transient activation: Because of specific complains against large (possibly infinite) expiry time from Mozilla and from Chrome internally, we changed the expiry time in our implementation to less than a minutes.

Please let me know if you have any questions.

Mustaq



--
You received this message because you are subscribed to the Google Groups "blink-dev" group.

Rick Byers

unread,
Aug 28, 2018, 2:02:03 PM8/28/18
to Mustaq Ahmed, Yoav Weiss, Chris Harrelson, Travis Leithead, Boris Zbarsky, Matt Giuca, blink-dev, Philip Jägenstedt, Domenic Denicola, Ojan Vafai, Domenic Denicola
Yoav, Chris and I discussed this in the API owners meeting today.

We continue to agree with the general strategy of trying to test (by shipping in chromium) the hypothesis that a much simpler model might be adequate to meet UX goals. In particular, to avoid the status quo of each browser having it's own complex set of heuristics we should add complexity to the model only to support specific cases which are considered UX blockers by some major browser. For chromium, the BIP issue is the only outstanding such issue we're aware of. It's on Mustaq to drive a resolution to that issue (escalating any difference of opinion between teams to Chrome leadership for a decision), and so this intent is blocked (at least) on getting a resolution to the BIP issue.

For Firefox, we certainly welcome any specific proposals to alter/extend the model to address any UX limitations Firefox determines to be an adoption blocker. We'd also be interested in any proposal that makes the model easier/safer to evolve in the future without adding complexity now that's not yet proven to be a hard requirement. But we expect the process of reaching interop in this complex space will be an ongoing journey determined in part by what we learn from trying to ship simpler models (eg. how will bad actors attempt to take advantage of the weaker model?). So we don't want to block chromium's progress on issues which MIGHT be adoption blockers. I.e. we suspect the fastest path to eventual interop probably starts with chromium shipping the simplest possible model we can stomach and then adding complexity from there only as its' proven to be strictly necessary. To me that means we should continue the classification and token-less model debates in parallel (based on data from real sites/users) without blocking this intent.

FWIW this all follows from the key insight which started this whole approach a couple years ago - Edge shipped with a VASTLY simpler system than Chrome and Firefox apparently without causing significant product problems for them. Perhaps +Travis Leithead would have something to contribute?

Rick

Dominick Ng

unread,
Aug 28, 2018, 7:05:34 PM8/28/18
to Rick Byers, Mustaq Ahmed, yo...@yoav.ws, Chris Harrelson, travis....@microsoft.com, bzba...@mit.edu, Matt Giuca, blink-dev, Philip Jägenstedt, d...@domenic.me, Ojan Vafai, dom...@chromium.org
I think we have a resolution to the primary BIP issue (change the API to consume the gesture), which we can do fairly easily. The bug also raised questions about the token passing, but I think discussing that in parallel to this intent is fine as Rick suggested.

Boris Zbarsky

unread,
Aug 29, 2018, 11:55:11 AM8/29/18
to Mustaq Ahmed, Matt Giuca, blink-dev, Philip Jägenstedt, Domenic Denicola, Rick Byers, Ojan Vafai, Domenic Denicola
On 8/24/18 5:11 PM, 'Mustaq Ahmed' via blink-dev wrote:
> - *Expiry of transient activation:* Because of specific complains
> against large (possibly infinite) expiry time from Mozilla
> <https://github.com/whatwg/html/issues/1903#issuecomment-324968512> and
> from Chrome internally, we changed
> <https://chromium-review.googlesource.com/c/chromium/src/+/1178746> the
> expiry time in our implementation to less than a minutes.

Please note that in Mozilla's case the main place we apply an expiry
time it's set to 1 _second_. This may obviously not be appropriate for
all APIs, but the current spec draft doesn't seem to envision having
different timeouts for different APIs...

-Boris

P.S. Sorry for the delay; I just got back from vacation and am catching
up on things.

Mustaq Ahmed

unread,
Sep 11, 2018, 11:26:56 AM9/11/18
to Rick Byers, bzba...@mit.edu, Matt Giuca, blink-dev, Philip Jägenstedt, Domenic Denicola, Ojan Vafai, Domenic Denicola, domi...@chromium.org
A quick update: dominickn@ has already landed the mentioned fix for the intent blocker BIP issue.

Re a fixed timeout: we understand that a API-specific timeout could be considered useful but our experiments so far didn't show any breakage caused by our proposed fixed-30sec timeout.  So we would like to stick to a simpler model now (and we can change things in future if we encounter a specific need).

Boris Zbarsky

unread,
Sep 11, 2018, 11:38:07 AM9/11/18
to Mustaq Ahmed, Rick Byers, Matt Giuca, blink-dev, Philip Jägenstedt, Domenic Denicola, Ojan Vafai, Domenic Denicola, domi...@chromium.org
On 9/11/18 11:26 AM, Mustaq Ahmed wrote:
> Re a fixed timeout: we understand that a API-specific timeout could be
> considered useful but our experiments so far didn't show any breakage
> caused by our proposed fixed-30sec timeout.

I think I wasn't clear.

Firefox does not consider a 30sec timeout acceptable for the full-screen
API as a matter of user safety. There is too much potential for abuse
there; the fullscreen prompt needs to be clearly connected to the user
action in the user's mind, and a 30-second lag is far too long for that.

So to have an API with a single uniform timeout we would need to have a
a 1sec timeout across the board (and some confidence that there is no
breakage from that).

> (and we can change things in future if we
> encounter a specific need).

I am pointing to a specific need now, unless the proposal is to have a
1sec timeout across the board.

-Boris

Daniel Bratell

unread,
Sep 17, 2018, 9:10:28 AM9/17/18
to Mustaq Ahmed, Rick Byers, Boris Zbarsky, Matt Giuca, blink-dev, Philip Jägenstedt, Domenic Denicola, Ojan Vafai, Domenic Denicola, domi...@chromium.org
I'm coming in a bit late here so I might have missed something, but I
wonder if a pure timer based solution can be both secure and usable. A
long timeout and you get malicious abuse, a short timeout and you get
sites breaking randomly due to slow hardware, resource contention or
something else that introduces delays.

Usually it's ok for a sight to do something as a direct response to a user
action. "Direct" is a bit hard to define, but a reasonable interpretation
is that nothing else should happen between the user action and the
response. Today that is partly handled by a consumable token following the
chain of events. Will that go away, and if so, is there any other system
that can connect an action with the result?

A timer might still be good, especially to limit chains of async events
(if those should be allowed at all?), but onclick="window.open()" should
really work regardless how slow a computer is.

/Daniel

--
/* Opera Software, Linköping, Sweden: CEST (UTC+2) */

Boris Zbarsky

unread,
Sep 17, 2018, 11:00:43 AM9/17/18
to Daniel Bratell, Mustaq Ahmed, Rick Byers, Matt Giuca, blink-dev, Philip Jägenstedt, Domenic Denicola, Ojan Vafai, Domenic Denicola, domi...@chromium.org
On 9/17/18 9:10 AM, Daniel Bratell wrote:
> I'm coming in a bit late here so I might have missed something, but I
> wonder if a pure timer based solution can be both secure and usable. A
> long timeout and you get malicious abuse, a short timeout and you get
> sites breaking randomly due to slow hardware, resource contention or
> something else that introduces delays.

That's true. In the end this is a UX decision, not a purely engineering
one.

> Usually it's ok for a sight to do something as a direct response to a
> user action. "Direct" is a bit hard to define, but a reasonable
> interpretation is that nothing else should happen between the user
> action and the response.

Again, this is a UX decision....

-Boris

Mustaq Ahmed

unread,
Sep 18, 2018, 12:18:04 PM9/18/18
to bzba...@mit.edu, Daniel Bratell, Rick Byers, Matt Giuca, blink-dev, Philip Jägenstedt, Domenic Denicola, Ojan Vafai, Domenic Denicola, domi...@chromium.org
Hi Boris & Daniel:

Thanks for pointing out specific cases to ease the discussion.  Let me try to address them inline below.

Mustaq


On Tue, Sep 11, 2018 at 11:38 AM Boris Zbarsky <bzba...@mit.edu> wrote:
...

Firefox does not consider a 30sec timeout acceptable for the full-screen 
API as a matter of user safety.  There is too much potential for abuse 
there; the fullscreen prompt needs to be clearly connected to the user 
action in the user's mind, and a 30-second lag is far too long for that.

So to have an API with a single uniform timeout we would need to have a 
a 1sec timeout across the board (and some confidence that there is no 
breakage from that).

> (and we can change things in future if we 
> encounter a specific need).

I am pointing to a specific need now, unless the proposal is to have a 
1sec timeout across the board.

As you suspected, 1sec timeout "across the board" doesn't work except for the simplest cases (by "simplest" I mean calling popups or fullscreen etc directly from within an activation event handler, say "click").  Here are some specific examples, all seem like deliberate implementations IMHO:
  • For opening a popup through a Promise, Edge & Safari apparently allows infinite time to resolve the Promise (btw, even an immediate resolution fails in Firefox).
  • Blink tokens expire at 10 secs for certain out-of-process cases.
Of course that was the easiest part of the discussion because you and I most likely agree on this already.

Now the hard part: if we don't allow more-than-a-second across the board, there must be API specific expiry times as you have argued, with a longer timeout reserved for slow/async APIs like Promise/XHR.  My past replies mention one main reason we want to avoid such complication: it would be impossible for the Web to converge to a such complex solution.  Here is my second argument against API-specific expiry, based on chaining of APIs with short and long expiry times:
  • Suppose a "click" handler adds a Promise to call fullscreen following a possibly-slow (say ~10sec) Promise-resolution, and at the same time, adds a setTimeout call to call fullscreen soon (within, say, 0.2sec).  Assume that each of the fullscreen calls is valid and non-malicious (gated with some conditional argument to avoid duplicates). The gesture token must have been created at the time the user clicked the mouse, and the browser don't know which call would succeed until actually executing the code.  Which expiry time would the browser choose for the token?
    • If the expiry is 1sec as per your restriction, the promise would never succeed.  That doesn't seem acceptable, right?  (Here is a popular Chrome bug about it).
    • If the expiry is 10secs because of the Promise chain, we are breaking the proposed restriction that fullscreen must not be successful after 1sec.
  • An alternate way to resolve this would be a (yet-to-materialize) complete implementation of current spec: the token would be available only within the timeout function and then only within the Promise chain (i.e. only within the time interval [0,1] + [x, x+1] where x is the moment the Promise is resolved).  This has its own problem: a site can "fill" the whole [0-10] time interval by crafting repeated Promise calls, effectively making fullscreen calls doable anytime in the 10sec interval (Matt first showed this idea here).
This argument applies to chaining of any short- and long-expiry APIs.

Therefore, I don't see any reason we must choose between "1-sec-across-across-the-board" and "API-specific expiry time" solutions.

Again UAv2 is one model that seems to work for us (Chrome).  We are open to any proposal that can fix the current (broken) state of the Web.



On Mon, Sep 17, 2018 at 9:10 AM Daniel Bratell <bra...@opera.com> wrote:
... 
Usually it's ok for a sight to do something as a direct response to a user  
action. "Direct" is a bit hard to define, but a reasonable interpretation  
is that nothing else should happen between the user action and the  
response.

Yes, defining a "direct response" is a bit too hard, specially from user's perspective.  We (browsers) can't guarantee "nothing else" would happen between action and response, even if we restrict tokens to only within event handlers (i.e. even if neither setTimeouts nor postMessages nor Promises are allowed): the "nothing in-between" is perceived by the user and defined by the developer.  If the click handler starts animating the whole page like crazy then calls fullscreen, should we (browsers) not allow the fullscreen call because the user got enough distraction already?  How will we detect it?

Defining direct response is harder for a browser also in the case an event handler queues multiple tasks (e.g. as in my Promise example above): which of the queued tasks should be treated as direct vs indirect?

All we (browsers) can do it to limit possible abuses.  Part of the problem is that the Web has absolutely no definition of how APIs "use" user activation, and our proposal shows one way: we can "grade" the abusiveness of APIs through the classification, possibly allow/disallowing certain sequence.  This is certainly an improvement over the current situation, see below...
 
Today that is partly handled by a consumable token following the  
chain of events. Will that go away, and if so, is there any other system  
that can connect an action with the result?

We are not proposing to take away the "chain of events" you mentioned, instead claiming that the chaining doesn't "reasonably exist" in the Web today:
  • Chrome: setTimeout doesn't chain after depth 1, promises fail with OOPIFs, postMessages to OOPIFs deliberately drops tokens after the first call.
  • Edge: neither setTimeout nor postMessages work, Promises have infinity expiry.
  • Firefox: neither postMessage nor Promise work.
  • Safari: Like Chrome with infinite expiry for Promises (didn't try OOPIFs).
There's more to cover: didn't try MessageChannels (we know that Chrome fails), or any other "chaining mechanism" already shipped today.

We are offering an API-agnostic and a possibly interoperable way to make it work consistently across the board (i.e. w/o API specific impl work).

The token passing idea is there for ~a decade already, and all browsers we checked are incomplete in terms of chaining.  When will we be all done? Nobody knows.  We are proposing a way out of this.


Another issue with simple chaining/token-passing is that it can leak information to untrusted sites if the token is not restricted correctly.  (I mentioned above) Chrome postMessage tokens to OOPIFs are dropped except for the first call, for this reason.  This is buggy: what if the first call didn't even use the token but the second call does?

With UAv2, the default activation visibility is defined through frame-hierarchy, and we can (in future) have a new feature policy to allow a site to redefine the visibility declaratively.  This looks like a better way to handle untrusted sites than having browser hacks/heuristics.


A timer might still be good, especially to limit chains of async events  
(if those should be allowed at all?), but onclick="window.open()" should  
really work regardless how slow a computer is.

Setting the expiry time based on the speed of computer/network etc is something we can try, specially to support slow network and low config phone use cases in emerging markets.  Thanks for the idea!  (We tried a (hardcoded) 1hr time limit to test if an infinite time limit plus a single popup per activation works.  It didn't, and we saw arguments against it, so we aborted it.)

With UAv2 spec proposal, we left the expiry time as implementation details.  Adding controls like this could be a future work/follow-up spec.

Philip Jägenstedt

unread,
Sep 18, 2018, 12:18:34 PM9/18/18
to Boris Zbarsky, Daniel Bratell, Mustaq Ahmed, Rick Byers, Matt Giuca, blink-dev, Domenic Denicola, Ojan Vafai, Domenic Denicola, Dominick Ng
Chris, Daniel, Yoav and I discussed this a bit in the API owners weekly today.

Mustaq, what are your thoughts on shortening the timeout? Do we have
histograms of how long it takes the user activation tokens to be
consumed currently?

Boris Zbarsky

unread,
Sep 18, 2018, 1:28:23 PM9/18/18
to Mustaq Ahmed, Daniel Bratell, Rick Byers, Matt Giuca, blink-dev, Philip Jägenstedt, Domenic Denicola, Ojan Vafai, Domenic Denicola, domi...@chromium.org
On 9/18/18 12:17 PM, Mustaq Ahmed wrote:
> * Suppose a "click" handler adds a Promise to call fullscreen
> following a possibly-slow (say ~10sec) Promise-resolution, and at
> the same time, adds a setTimeout call to call fullscreen soon
> (within, say, 0.2sec).  Assume that each of the fullscreen calls is
> valid and non-malicious (gated with some conditional argument to
> avoid duplicates). The gesture token must have been created at the
> time the user clicked the mouse, and the browser don't know which
> call would succeed until actually executing the code.  Which expiry
> time would the browser choose for the token?
> o If the expiry is 1sec as per your restriction, the promise would
> never succeed.

Correct. That is the behavior Firefox UX wants in this situation: if
the fullscreen request is too far removed from the user activation
trigger in time, it should not succeed.

> That doesn't seem acceptable, right?

It's acceptable as a user safety feature.

> (Here <http://crbug.com/404161> is a popular Chrome bug about it).

That's a bug about promises in general, not promises that get resolved
arbitrarily far into the fugure, yes?

> * An alternate way to resolve this would be a (yet-to-materialize)
> complete implementation of current spec
> <https://html.spec.whatwg.org/#triggered-by-user-activation>: the
> token would be available only within the timeout function and then
> only within the Promise chain (i.e. only within the time interval
> [0,1] + [x, x+1] where x is the moment the Promise is resolved).

Even if this were implemented, it would not address the "fullscreen
request not tied to the click in the user's mind" problem the 1s timeout
is aiming to solve in Firefox.

> Therefore, I don't see any reason we must choose between
> "1-sec-across-across-the-board" and "API-specific expiry time" solutions.

I don't follow what you mean here.

Again, there is a UX, not engineering, requirement that fullscreen
requests be denied if they come more than 1s after the triggering user
event in Firefox. How do you propose we address this requirement.

> Again UAv2 is /one/ model that seems to work for us (Chrome).

That's fine, but if you expect other UAs to adopt it, including UAs that
make different tradeoffs on the user-safety-vs-site-functionality
spectrum, then it will need to be more flexible than it is now.

> We are open to any proposal that can fix the current (broken) state of the Web.

Here's a specific proposal that I believe would be sufficient for
Firefox's current needs, as a sort of diff to UAv2:

1) A "user activation" should include a creation timestamp.
2) APIs that check for existence of a "user activation" should be able
to compare that timestamp to the current time to decide whether the
token is expired for their purposes.

I guess there is an open question around whether such a check that
decides the activation is expired should also consume the activation if
the API would normally do so.

-Boris

Domenic Denicola

unread,
Sep 18, 2018, 1:32:34 PM9/18/18
to Boris Zbarsky, Mustaq Ahmed, Daniel Bratell, Rick Byers, Matt Giuca, blink-dev, Philip Jägenstedt, Ojan Vafai, domi...@chromium.org
Boris, to be clear, your argument is that we should trade off interoperability/predictability in favor of product flexibility so that different browsers can make different decisions. Right? E.g. repeated claims that this is a UX, not engineering, decision.

So in the world of your proposal, you would want Firefox to be able to use a different threshold with that timestamp than Chrome. Then web developers would have to test in each browser/network condition to figure out what works for most of their users.

Is that right?

Boris Zbarsky

unread,
Sep 18, 2018, 1:52:25 PM9/18/18
to Domenic Denicola, Mustaq Ahmed, Daniel Bratell, Rick Byers, Matt Giuca, blink-dev, Philip Jägenstedt, Ojan Vafai, domi...@chromium.org
On 9/18/18 1:32 PM, Domenic Denicola wrote:
> Boris, to be clear, your argument is that we should trade off interoperability/predictability in favor of product flexibility

Products will do what they think is right for their users. If the spec
doesn't allow that, they will violate the spec as needed: see popup
blocking, Chrome's interventions, Safari and Firefox's anti-tracking
work, etc.

If we know up front that people won't implement the spec as written,
writing it that way is a bit odd.

In the specific instance of user activation stuff, my general feeling is
that there are big open questions and I suspect a significant amount of
UI experimentation ahead of us still. We can have a spec flexible
enough to allow that, at least along axes along which we already know
people want to modify behavior. Or we can have a spec which nominally
doesn't allow that but end up with interventions and the like and
possible post-facto spec changes. Except in the latter case the spec
can end up being used as a lever to try to prevent experimentation and
behavior changes, of course...

> So in the world of your proposal, you would want Firefox to be able to use a different threshold with that timestamp than Chrome.

In an ideal world, Firefox and Chrome would agree on the threshold for
any given API. My main argument given the information I have today is
that this agreement point may be different for different APIs, because
different APIs present different threat models.

It's possible for browsers to fail to come to an agreement on tradeoffs
like this. Wouldn't be the first time (WebUSB, WebMIDI, media autoplay
behavior, handling of third-party cookies all come to mind immediately;
I am sure there are many more). I do think such failures are not great
for the web platform; I'm pretty sure you do too.

> Then web developers would have to test in each browser/network condition to figure out what works for most of their users.

This is highly undesirable, of course. It's a last resort if browsers
really just can't agree...

-Boris

Daniel Bratell

unread,
Sep 19, 2018, 9:14:09 AM9/19/18
to Domenic Denicola, Mustaq Ahmed, Boris Zbarsky, Rick Byers, Matt Giuca, blink-dev, Philip Jägenstedt, Ojan Vafai, domi...@chromium.org
The word "works" is used a lot in this thread but it's not clear to me
that it covers both the security aspect and the functionality aspect. The
whole system is there to protect the user from being tricked or abused so
the priority should be to keep that safety intact.

I agree with bzbarsky that 30 second timeouts will not protect the user
from sites that try to trick them or take advantage of them. It is a too
long time. I can not say what the limit for a "safe" delay is. It is
probably not even a constant but depends on a lot of circumstances making
it hard to specify.

Secondly: The stated purpose of the specification is to create a well
defined base for how this protection works so that well behaved sites have
something to depend on when they implement their sites. If other vendors
are not interested in adapting to it and are not compatible, it fails its
purpose. (If the new implementation is an improvement it would still be
fine to use, as an internal Chromium implementation detail.)

The same conflict with the purpose appears if the specification is not
specific enough. Let us say there is a vendor chosen grace period, then
nice sites still won't know what time budget they have for doing their
sensitive action since it depends on the browser vendor and the
specification won't help them.

/Daniel

Philip Jägenstedt

unread,
Sep 19, 2018, 9:37:04 AM9/19/18
to Boris Zbarsky, Domenic Denicola, Mustaq Ahmed, Daniel Bratell, Rick Byers, Matt Giuca, blink-dev, Ojan Vafai, Dominick Ng
On Tue, Sep 18, 2018 at 7:52 PM Boris Zbarsky <bzba...@mit.edu> wrote:
>
> On 9/18/18 1:32 PM, Domenic Denicola wrote:
> > Boris, to be clear, your argument is that we should trade off interoperability/predictability in favor of product flexibility
>
> Products will do what they think is right for their users. If the spec
> doesn't allow that, they will violate the spec as needed: see popup
> blocking, Chrome's interventions, Safari and Firefox's anti-tracking
> work, etc.
>
> If we know up front that people won't implement the spec as written,
> writing it that way is a bit odd.
>
> In the specific instance of user activation stuff, my general feeling is
> that there are big open questions and I suspect a significant amount of
> UI experimentation ahead of us still. We can have a spec flexible
> enough to allow that, at least along axes along which we already know
> people want to modify behavior. Or we can have a spec which nominally
> doesn't allow that but end up with interventions and the like and
> possible post-facto spec changes. Except in the latter case the spec
> can end up being used as a lever to try to prevent experimentation and
> behavior changes, of course...
>
> > So in the world of your proposal, you would want Firefox to be able to use a different threshold with that timestamp than Chrome.
>
> In an ideal world, Firefox and Chrome would agree on the threshold for
> any given API. My main argument given the information I have today is
> that this agreement point may be different for different APIs, because
> different APIs present different threat models.

Perhaps this is naive, but in the case of at least fullscreen and
popups, agreement on the threshold seems possible. Is 1 second not an
acceptable threshold for both APIs in both Firefox and Chrome? (Sorry
if I missed the disagreement.)

Are there other APIs where we already know there will be initial
divergence? What are some that definitely will require >1 second in
some browser?

Mustaq Ahmed

unread,
Sep 20, 2018, 1:40:00 PM9/20/18
to Philip Jägenstedt, bzba...@mit.edu, Daniel Bratell, Domenic Denicola, Rick Byers, Matt Giuca, blink-dev, Ojan Vafai, domi...@chromium.org
We decided to reduce the timeout, but first let me summarize the discussion so far to see why:

Arguments in favor of a long timeout:
  • Implementations bypassing 1sec limit: 10sec special case in Chrome, infinite expiry through Promise in Edge & Safari.
  • The expectation mentioned above that onclick="window.open()" should work with arbitrary delay.
Arguments in favor of a small (1sec) timeout:
  • A smaller timeout minimizes abusability.
  • Chaining-agnostic expiry is acceptable as a user safety feature (thanks Boris for the point).
  • Popup and fullscreen are the most-abused APIs, and there seems to be a consensus for a small timeout at least for these.

We looked into making a data-driven choice between the two but it is hard at this point:
  • A successful call to consume/check user activation doesn't necessarily mean the call is non-abusive, so we didn't add any histograms specifically for user activation.  We are planning to look into the rates at which users manually open blocked popups, but it will take time.
  • For emerging markets, Chrome Android histograms show that certain navigation time ("ready" to "commit") at least doubles vs US.  We have only a rough argument at this point: if an activation-gated API call is made through a navigation-dependent Promise, a 1-sec expiry can cause 18% rejection in country X (vs 7% in US) while a 10-sec expiry can cause 1.2% rejection in X (vs 0.35% in US).

We have decided to take an informed risk for sake of our interop goal: we will try a 1sec timeout across the board.  If it turns out to be problematic in any way (more reports of failures, unexpected changes in popups), we (Chrome) will use data to come up with a longer value and start a new spec issue.

Boris and Daniel: hopefully you are on-board now?

(The spec wording around the 1-sec expiry will need some attention I believe, but that's orthogonal to this intent.  Let's discuss that in the pull request.)


Boris Zbarsky

unread,
Sep 20, 2018, 1:52:32 PM9/20/18
to Philip Jägenstedt, Domenic Denicola, Mustaq Ahmed, Daniel Bratell, Rick Byers, Matt Giuca, blink-dev, Ojan Vafai, Dominick Ng
On 9/19/18 9:36 AM, Philip Jägenstedt wrote:
> Perhaps this is naive, but in the case of at least fullscreen and
> popups, agreement on the threshold seems possible.

I agree.

> Is 1 second not an
> acceptable threshold for both APIs in both Firefox and Chrome? (Sorry
> if I missed the disagreement.)

For fullscreen, I'm not sure whether it's acceptable in Chrome.

For popups, it's unclear whether it's acceptable in either one. Needs
telemetry or testing.

> Are there other APIs where we already know there will be initial
> divergence?

Other than fullscreen, nothing so far.

> What are some that definitely will require >1 second in
> some browser?

I don't have affirmative data saying >1 second is required for anything.
I'm not sure whether Mustaq does.

-Boris

Boris Zbarsky

unread,
Sep 20, 2018, 5:29:11 PM9/20/18
to Mustaq Ahmed, Philip Jägenstedt, Daniel Bratell, Domenic Denicola, Rick Byers, Matt Giuca, blink-dev, Ojan Vafai, domi...@chromium.org
On 9/20/18 1:39 PM, Mustaq Ahmed wrote:
> Boris and Daniel: hopefully you are on-board now?

I think so, assuming the across-the-board 1-sec expiry will end up being
sufficiently web-compatible...

-Boris

Daniel Bratell

unread,
Sep 21, 2018, 6:26:55 AM9/21/18
to Mustaq Ahmed, Philip Jägenstedt, Boris Zbarsky, Domenic Denicola, Rick Byers, Matt Giuca, blink-dev, Ojan Vafai, domi...@chromium.org
I'm still not sure this is achieving its own goal of creating a
predictable world, but if Chromium and Firefox both successfully implement
it, I guess it will. (So a nested if).

Philip Jägenstedt

unread,
Sep 28, 2018, 9:14:57 AM9/28/18
to Daniel Bratell, Mustaq Ahmed, Boris Zbarsky, Domenic Denicola, Rick Byers, Matt Giuca, blink-dev, Ojan Vafai, Dominick Ng
It sounds like we don't yet know of concrete APIs where vendors will ultimately want to diverge, and that it's possible that across-the-board 1-sec expiry will work.

I've also discussed wit Mustaq whether we can collect metrics to inform the decision about the number, but it's complicated by consuming vs. non-consuming uses of gestures. For the non-consuming case (Frame::HasTransientUserActivation) it's not certain that caller will do anything when true is returned. It's easy to find such cases in code search, such as in FrameLoader::StartNavigation or ClipboardCommands::CanWriteClipboard. To gather metrics, one has to measure when the true value ultimately has a web-exposed effect that wouldn't have happened for false. That's possible, but probably a lot of work.

So, trying 1 second and being fast to revert for regressions seems reasonable. If there are regressions, perhaps it will be possible to measure just those cases before trying again. And ultimately, there will be some level of use of long expiration that on inspection we wouldn't consider legitimate, so usage numbers alone won't be enough to inform the decision.

LGTM1

Chris Harrelson

unread,
Sep 28, 2018, 11:44:22 AM9/28/18
to Philip Jägenstedt, Daniel Bratell, Mustaq Ahmed, Boris Zbarsky, Domenic Denicola, Rick Byers, Matt Giuca, blink-dev, Ojan Vafai, Dominick Ng
LGTM2

Thanks for the very hard work on this feature and intent!

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAARdPYcV0Y6ZFOC9iaF5BDbPj8eT6fKXMfS4Y%3D3Tw2RQmbqpHg%40mail.gmail.com.

Rick Byers

unread,
Oct 1, 2018, 11:44:03 AM10/1/18
to Chris Harrelson, Philip Jägenstedt, Daniel Bratell, Mustaq Ahmed, Boris Zbarsky, Domenic Denicola, Matt Giuca, blink-dev, Ojan Vafai, Dominick Ng
LGTM3
Reply all
Reply to author
Forward
0 new messages