-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Why expose produceCropTarget at MediaDevices level? #11
Comments
Exposing the interface on
I don't think so, but possibly I am missing your point.
The intention is:
I'm using the working assumption that we agree on no1 (let's discuss oherwise) and moving to discuss no2. Consider the example of Chrome. Returning a Promise gives Chrome the flexibility to produce the CropTargets' underlying ID in what we call the "browser process". This means that when Using a Promise also makes it easy to convince ourselves that Chrome's implementation suffers no hidden race conditions. Consider if a Promise were not employed in this scenario:
In this scenario, it is required that the CropTarget D1 minted would be known to the cropTo() code-paths before D2 tries to call cropTo(), or that the cropTo() code-paths be robust to such reordering. This can be hard to reason about. But if we return a Promise, then we get no1 immediately, and we can resolve the Promise at our leisure, when it's patently obvious that the CropTarget has been fully propagated. Note that the Promise gives flexibility. If Safari's implementation can convince itself there is no race even if the CropTarget is produced immediately, then Safari can return an already-resolved Promise. Editorial: To make this thread more accessible, could you please edit the original post and add ```webidl before the code snippets before and ``` after them? |
Partial interfaces seem sufficient to me. Calling twice produceCropTarget on the same element is expected to produce the same CropTarget, which might be surprising given it is a method. As of promise attribute vs. sync attribute, from what I read, using promises here is to accommodate a particular browser implementation. In general, we reserve promises to asynchronous operations, like querying hardware, querying centralised resources, querying user input. This is clearly not the case here, CropTarget is nothing more than a unique ID.
Done |
How common is it for specs to plug additional attributes directly onto HTMLElement? Checking the Chromium codebase just because that's the quickest way for me, it seems like it's exceedingly rare. I expect that the Chromium implementation is a good approximation of what W3C specifications do, but if my hack at getting this information was misguided, please let me know. As for extending HTMLElement, that's of course quite common - but that's not what you're proposing. :-) Btw, is it actually possible to expose a new field on all
Could you please clarify how this edge-case would be problematic? The way I see it, if a given context can get a reference to the object in order to supply it as a parameter to
I don't think that's surprising. We can also reduce the (IMHO already low) surprise factor by renaming to
Politely disagree. It's done to afford all implementations the flexibility to implement efficiently and simply. If some implementations don't require this flexibility, I think it wouldn't harm them either - simply return the Promise pre-resolved. |
I don't really know, it probably depends how much HTMLElement is actually used in APIs.
I am not exactly sure which cost you are talking about. Is it that HTMLElement would get bigger in memory?
It would need to be tested but if frame is detached, chances are the promise will reject.
There is a tradeoff with promises, we should use them when/if they are needed, which does not seem to apply here.
Step 1 & 2 can be replaced by:
|
It makes it much easier for Chrome to deliver a more efficient implementation, without costing anything for other browser vendors (return a pre-resolved Promise) or Web developers (await cropTo). The tradeoff seems favorable to me.
Who is C in this scenario? If A in the document that holds the crop-target (div, iframe, etc.), then it will produce the CropTarget (the token) and send it to document B, who holds the track. B will call cropTo directly. I don't follow where C fits into the picture, except possibly as a conduit for messages which can be neglected¹. What am I missing? -- |
A promise has a cost for web developers and for devices executing them.
C is the process producing the screen frames and is the one that needs to identify the actual display layer the element refers to. My understanding is that this process is separated from A and B.
cropTo is already an asynchronous operation and can handle the resolution of CropTarget. |
(I am sure Youenn knows, but if others want to join the conversation, one line of background: In Chrome, applications run in a "render process", and there is a "browser process" coordinating them. Cross-origin applications run in different processes.) Back to the produceCropTarget Promise.
As mentioned, Chrome's implementation problems are Chrome's problems, but (a) I suspect other implementers will have similar issues and (b) the cost of resolving these issues is negligible. |
That is where we differ in the analysis.
The central repository is an implementation choice that is a potential source of issues (memory for instance as you pointed out), it seems it should be avoided if possible. In our particular case, the spec does not define any such central repository. D2 will transmit this information to browser process at the time of cropping. The spec is light in how/when cropTo is supposed to validate crop targets, I'll file a separate issue. |
A central repository is an implementation detail. The spec should not accidentally disallow reasonable implementations (hence the Promise). The spec is not concerned with such details beyond that point. It's undesirable to put in the token more information than strictly necessary. By ensuring that CropTargets do not encode information about their source, we can provide better guarantees about the safety of the entire mechanism. (Please note - information not exposed to JS, but potentially available to a malicious application which has taken over the process, is also undesirable. Information not encoded in the render process cannot leaked to a malicious application.)
If my current comment has not been sufficient to convince you, you're welcome to continue the discussion in a spin-off issue. I wonder if anyone else (@jan-ivar, @aboba, @alvestrand) has an opinion to voice about exposing as |
I have done that in #17. |
Thanks for spinning off. Would the following be a good summary of the discussion that is remaining in this thread?
|
requestFullScreen API attached to Element directly is well encapsulated through partial interface.
SecureContext can be applied to methods as well as interfaces so this seems orthogonal.
Using promises is largely orthogonal in that case. |
Unless we've missed a good argument in either direction, it seems to me like the decision boils down to a matter of preference.
|
I don't think it is solely a matter of preference.
Getting WG input is always useful, too late for next meeting though probably. |
@eladalon1983 AFAICT Youenn prefers Element.cropTarget, not Element.cropTarget(). I think he makes a good case for why an attribute is most idiomatic given the properties we want. However, there are a LOT of Elements compared to the rarity of needing a cropTarget from one — 0.005% of pageloads use Presumably, the cropTarget would be instantiated in the getter if it doesn't exist, rather than at Element construction time, but this getter might be implicitly called by code libraries or even browser tools interrogating the Element (e.g. dumped to web console). That said, there's precedent for rarely used features in Element.requestFullscreen() which is a method, so maybe Element.cropTarget() would be the right tradeoff? cc @annevk Thoughts on this? |
Element.requestFullScreen() is rightfully a method, it returns a different promise every time it is called. FWIW, a cropTarget when created should be nothing more than a weak reference to its element (when being transferred or when used in cropTo is when additional steps might be executed). Hence why I do not see accidental code libraries CropTarget creation as an issue. |
Triggering accidental lazy creation of CropTarget-s for all Elements in the DOM is patently undesirable. First, for the memory it consumes; that much should be self-evident. Second, for the side-effect; elaboration follows. Let's define as "active" a CropTarget which is being used - that is, there is some track which was cropTo()-ed with that CropTarget. An "active" CropTarget has to be tracked along the rendering pipeline in every frame, which requires some work from the UA. Unless |
It would be interesting to understand whether libraries actually go through all elements and get all their attributes. Thinking a bit more though, I don't see why the spec mandates to expose a single CropTarget per Element. |
Could you please explain why this is a problem for implementers? |
Generally the "iterate over all members" pattern affects |
I don't see a big advantage in exposing produceCropTarget() on a plethora of objects. Having the function be in one place makes much more sense to my impression of understandability. |
To document some out-of-band discussions, I am OK with the spec mandating returning an equivalent CropTarget, which is a less stringent requirement than "the same" CropTarget. |
It is not on a plethora of objects, it is only either in MediaDevices prototype or HTMLElement prototype. We discussed with @eladalon1983 some of the reasons why element was a better location during last editor's meeting.
|
I think what @alvestrand was referring to, is that (i) this would now be exposed on many different sub-types of HTMLElement, as well (ii) exposed on multiple instances. If that's what he meant, I agree.
That was an interesting case, but unclear to me how important it is to keep supporting non-secure contexts. Would they be able to
What's the mechanism for that?
I don't think there has to be a connection between where the CropTarget-minting-API is exposed, to where the elements are, let alone where the CropTargets are.
Could you clarify what you mean here? I couldn't parse this in a manner consistent with the rest of the message. I mean, if there is a way to define produceCropTarget as exposed on the class MediaDevices, but not tied to any object, then that's fine by me...
Why would I need to call the function? Why can't I just check for its existence using |
I represent a team at Google that has been using the Region Capture API internally. We prefer mediaDevices.produceCropId (vs Element.produceCropId()) because it's easier to feature-detect whether the API is available without needing an Element to detect the method presence. Hope that helps! |
With regards to point 1::
|
What's a "neutered mediaDevicees"? Since CropTarget is (by design) useless without a captured MediaStreamTrack, and getDisplayMedia() is a mediaDevices function, the only case where this matters is when mediaDevices is enabled in the capturer but disabled (how?) in the capturee. |
https://jsfiddle.net/8a0qsf35/ is a good example (detached iframe).
SecureContext is one example where MediaDevices might not be available in the capturee. These two examples show that putting this API in MediaDevices interface or mediaDevices instance creates unnecessary issues. The underlying issue is that this API shape ties Elements with unrelated MediaDevices objects. |
@eladalon1983 I already answered that above in #11 (comment) and #11 (comment). |
Those comments criticize the current API shape from multiple directions, but do not explain how accommodating implementers here, would come at the expense of higher constituencies. Unless you mean to say that developer confusion is your claim of harm to higher constituencies? Is that your argument? If so - is it your only argument wrt priority of constituencies, or are there more? |
This thread is about where to put the API, be it sync or async, let's focus on that. |
I am OK migrating the discussion there. But I still want it continued. What can be brought up four times, can be explained once. |
If exposing to Element is not good for documentation, and given exposing it to MediaDevices has shortcomings, the best approach might to add the API directly within CropTarget:
|
This is aesthetically pleasing. Let me check one of my concerns first, right after I finish something unrelated, and I'll be back to discuss this. But to save us an iteration - I'll probably want this to fail in insecure contexts. Your thoughts? |
If I understand correctly, we would get something like this: A static method from an interface that would return the interface. [
SecureContext,
Exposed=(Window,Worker),
Serializable
] interface CropTarget {
static Promise<CropTarget> fromElement(Element element);
}; This would be the first time we introduce this pattern to the Web Platform. |
I don't think that's accurate. We just specced |
You're right @domenic. I was looking for |
Shall we go with @youennf's suggestion of |
@jan-ivar , wdyt? |
FWIW, I am ok compromising on regrouping API in CropTarget as I proposed earlier, as long as we can revisit the exact API shape once the sync/async issue is resolved. |
@youennf getting the irrelevant |
@jan-ivar, does that mean that if I send a PR to move the point of exposure to CropTarget.fromElement(Element), you'll approve it? Note that I'll keep it async, which is the status quo, and maintain the same note about it being a contested issue. |
That seems like a good way to separate these issues. |
CropTarget should probably not be SecureContext, nothing prevents CropTarget to be serialized from a secure context to a non secure context AFAIK. |
The status quo of the document is to only expose token-minting in secure contexts. This follows directly from the minting-API being exposed on |
I'm going to send a PR with this shape: [Exposed=(Window,Worker), Serializable]
interface CropTarget {
[SecureContext] static Promise<CropTarget> fromElement(Element element);
}; This ensures the delta from the current spec is purely in the point of exposure (from MediaDevices objects to a static method of CropTarget.) |
I've pushed PR w3c/mediacapture-screen-share#50 for review. It changes as little as possible from the status quo; literally a copy-paste with s/produceCropTarget/fromElement executed on it. I suggest we land that, close this issue, and proceed with matters of polish (the prose can be improved) and substance (e.g. w3c/mediacapture-screen-share#17) as a follow-up. PTAL. |
I agree with the proposed solution (minimal change). |
@eladalon1983 the way I interpreted the compromise above was we agreed to move the point of exposure from MediaDevices (a SecureContext object) to CropTarget (which is not). Adding
@youennf already mentioned in #11 (comment) that this was part of the issue: "MediaDevices is SecureContext, Element is not. It seems ok for a non secure document to be able to create a CropTarget", so these were known stakes.
@alvestrand In the interest of progress, I'll accept the PR with a note on the lack of consensus over SecureContext. |
Then we have different interpretations. I still suggest we start by merging w3c/mediacapture-screen-share#50, then continue the discussion about SecureContext separately. Should we decide to move away from SecureContext, we'll then have the path for it (which we didn't with mediaDevices.produceCropTarget).
I understand Youenn's reasoning and I have addressed it.
That's great. |
Closing this issue now that the API moved to CropTarget. |
Closing.
For posterity - w3c/mediacapture-screen-share#69. |
Looking at the algorithm, it seems the same CropTarget object will be created if produceCropTarget is called several times on the same element.
That would probably be specified by adding a CropTarget slot on HTMLElement directly.
This would also solve probably the cloning element cropTarget issue.
The second question is whether below API might not be better suited:
I am also wondering why we even need a promise there.
It seems implementations could produce a cropTarget synchronously without any IPC, which would end up with:
The text was updated successfully, but these errors were encountered: