Skip to content
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

Adds "audioworklet" and "paintworklet" as destinations. #527

Merged
merged 1 commit into from
Nov 1, 2017

Conversation

bfgeek
Copy link
Member

@bfgeek bfgeek commented Apr 14, 2017

These two are the ones which are being implemented, so starting with
them for now.


Preview | Diff

@bfgeek
Copy link
Member Author

bfgeek commented Apr 14, 2017

@mikewest @annevk

@annevk
Copy link
Member

annevk commented Apr 15, 2017

This looks reasonable, apart from the CSP directive as discussed in w3c/webappsec-csp#203. We should probably sort that through first.

@mikewest
Copy link
Member

I'm fine with adding these. Do we need to distinguish the worklet types in Fetch? That is, do you anticipate controlling the different types of worklets via distinct policy directives? Or would a single worklet destination be enough to start with?

@annevk
Copy link
Member

annevk commented Apr 15, 2017

They are somewhat distinct in that one can have postMessage() and a single global, and the other cannot and can have multiple globals, but yeah, they can probably be governed by a single policy I'd think.

@annevk
Copy link
Member

annevk commented Aug 30, 2017

This will need to be rebased now that we removed type. And child-src needs to become script-src.

I'm not quite sure requests for worklets count as non-subresource requests. That would mean they get their own service worker rather than the service worker associated with the document they're loaded from. I suspect you actually want the latter.

cc @jakearchibald

@bfgeek
Copy link
Member Author

bfgeek commented Sep 1, 2017

Rebased, with just script-src

Copy link
Member

@annevk annevk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @bfgeek! This looks pretty solid to me, apart from a potentially substantive issue around service worker matching.

fetch.bs Outdated
or "<code>worker</code>".
whose <a for=request>destination</a> is "<code>audioworklet</code>", "<code>document</code>",
"<code>paintworklet</code>", "<code>report</code>", "<code>serviceworker</code>",
"<code>sharedworker</code>", or "<code>worker</code>".
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this what we want for worklets? This influences the way a service worker is selected for them. I'd think that we just want to reuse the service worker of the document for worklets.

cc @jakearchibald @jungkees

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I thought this required additional input from @jakearchibald & @jungkees I've updated the PR.

@annevk
Copy link
Member

annevk commented Sep 25, 2017

You'll need to rebase since again since we modified RequestDestination I suspect. Unfortunately I'm not able to do it for you.

@annevk
Copy link
Member

annevk commented Sep 25, 2017

@padenot do you want a tracking bug for this against Gecko to make sure we don't forget about it when adding audio worklets?

@bfgeek are there tests for this?

@padenot
Copy link

padenot commented Sep 25, 2017

Thanks for heads up @annevk, now filed as bug 1402784, linked to the main AudioWorket bug.

@jakearchibald
Copy link
Collaborator

Each call to addModule creates a new global and potentially runs in different thread. I can't see any way for a worklet to make a request, but that may happen in future. It's also unclear what would happen if the worklet response had a header that initiated a request (eg Preload:).

If the above is correct, they feel very similar to dedicated workers, so I think they're a client, so they should be added to "non-subresource request".

Worklets don't support postMessage right? If we add them to the clients API we'd need to take that into account.

@jungkees
Copy link
Contributor

A worklet can have multiple associated WorkletGlobalScopes, right? To my understanding, calls to addModule trigger fetching a script from each of the associated global. A WorkletGlobalScope is its own script execution context, and fetching a script for that sounds a main resource fetch to me. So, matching a SW for that fetch sounds reasonable to me.

@annevk
Copy link
Member

annevk commented Sep 25, 2017

@jakearchibald good questions. https://webaudio.github.io/web-audio-api/ which defines audio worklets also defines MessageChannel for them, but paint worklets won't have that. Where is Preload defined? Is that Link: <...>; rel=preload. Either way, that already shouldn't always apply I think so whatever defines that ideally has already taken this into account by not using overly generic language.

@jakearchibald
Copy link
Collaborator

Ugh yes I mean Link: <...>; rel=preload sorry.

@nhiroki
Copy link

nhiroki commented Oct 5, 2017

Worklets are loaded as module scripts, so they can make a request by the dynamic import.

Worklets can be loaded from a remote origin, so having their own Service Worker sounds reasonable.

// On http://example.com/
worklet.addModule("http://example.com/worklet.js");  // Same-origin. OK.
worklet.addModule("http://www.example.com/worklet.js")  // Remote-origin, but OK.

@annevk
Copy link
Member

annevk commented Oct 5, 2017

Hmm, loading from a remote origin seems at odds with service workers. I don't think we want to launch a service worker from a different origin for these kind of requests. That might actually be problematic for module workers too.

cc @domenic

@nhiroki
Copy link

nhiroki commented Oct 6, 2017

Just in case, workers cannot load a script from a remote origin:

  1. Let worker URL be the resulting URL record.

Note: Any same-origin URL (including blob: URLs) can be used. data: URLs can also be used, but they create a worker with an opaque origin.

https://html.spec.whatwg.org/multipage/workers.html#dom-worker

@annevk
Copy link
Member

annevk commented Oct 6, 2017

@nhiroki module workers have no such restriction. That note is incorrect for them. (Though given how service workers work they probably need to have the same restriction. But that also makes me think that we don't want worklets to be non-subresources.)

@annevk
Copy link
Member

annevk commented Oct 10, 2017

Filed whatwg/html#3109 on the module workers problem.

I think the best course of action is for worklets to be subresources, though it wold be good for @mikewest to chime in to say if that would harm CSP in anyway. I think referrer we can work around, whenever worklets start fetching resources on their own. The one invariant this will impose on worklets is that they can only ever have a single owner. I hope that's okay @bfgeek.

@jakearchibald
Copy link
Collaborator

Following from whatwg/html#3109 (comment), could we make worklets properly cross origin?

@mikewest
Copy link
Member

I think the best course of action is for worklets to be subresources, though it wold be good for @mikewest to chime in to say if that would harm CSP in anyway.

Treating worklets as subresources seems reasonable. I agree with @annevk's suggestion above that they should not have a distinct service worker, and that we should generally treat them as being part of the document in which they're embedded for purposes of referrer policy and (eventually) CSP restrictions on requests they might make in the future.

Following from whatwg/html#3109 (comment), could we make worklets properly cross origin?

What would that mean? My understanding of worklets is that they're basically scripts executing in the same origin as the document (basically weirdly-behaved <script> elements). Would you suggest that we put them in a distinct origin? Would they still have access to paint onto their containing origin's page? That seems strange.

@nhiroki
Copy link

nhiroki commented Oct 10, 2017

I think the best course of action is for worklets to be subresources, though it wold be good for @mikewest to chime in to say if that would harm CSP in anyway.

How about dedicated workers? Should we change them to subresources, too?

Note that, in the current Chromium implementation, dedicated workers don't have their own service worker. They are controlled by parent document's service worker (https://crbug.com/731599). AFAICS, Firefox does the same behavior, so it could relatively be easy to change them to subresources in terms of SW interception.

@annevk
Copy link
Member

annevk commented Oct 10, 2017

Should we change them to subresources, too?

We could, especially if that matches implementations. I think we should consider that in isolation though; would you mind filing an issue at https://github.com/whatwg/html/issues/new?

@jakearchibald
Copy link
Collaborator

@mikewest

What would that mean? …

Yeah, it doesn't make sense. Worklets as subresources makes sense to me.

@nhiroki
Copy link

nhiroki commented Oct 10, 2017

@annevk Filed whatwg/html#3112

@wanderview
Copy link
Member

Wait, don't worklets create globals? Is there any precedent for a subresource that creates globals? This would seem to make the top level entities to me.

@domenic
Copy link
Member

domenic commented Oct 11, 2017

Your example seems no different from

import changesSomething from "./changes-something.js";

// PaintWorkletGlobalScope
registerPaint('ripple', class {
  paint(ctx, geom, ...) {
   if (geom.width ==12345) {
    changesSomething();
   }
  }
});

// changes-something.js
export default () => {
  registerPaint('foo', class {
    paint() { /* some valid paint code */);
  });
};

What am I missing?

(You could also avoid even static import by just inlining the contents of that file into the if statement. The real problem here seems to be if statements, not module importing.)

@wanderview
Copy link
Member

It feels like addModule is a subresource fetch for an already existing global, like loading scripts in an about:blank iframe. The opaque origin thing is a shame though, as it means these can't work offline.

Since the modules are fetched from the owner's Client they will be intercepted if that Client is controlled. So I think it will work offline. (Sorry if this is duplicate. Github is confusing the order of messages or something today.)

@jakearchibald
Copy link
Collaborator

@domenic they're static across all threads.

Say you addModule('foo'), the browser may create 10 globals for that, but they're all going to import the same static modules.

But if one of those globals calls import('bar'), now that single global has different code.

It's kinda the same as:

if (Math.round(Math.random()) {
  // change behaviour
}

@domenic
Copy link
Member

domenic commented Oct 11, 2017

@jakearchibald as I tried to illustrate above, you don't need import() to invoke different code in different globals. You just need if statements and functions. import() seems no better or worse than other functions.

@jakearchibald
Copy link
Collaborator

@domenic a static import will hold back the execution of the worklet (or the addition of that module). Whereas with import(), the worklet will execute and be used, then the module will load at some point and behaviour will change.

But yeah, you could create the same problem with setTimeout.

@wanderview
Copy link
Member

But setTimeout() is not exposed on WorkletGlobalScope.

@domenic
Copy link
Member

domenic commented Oct 11, 2017

I think I see. I'm happy to work with you all and TC39 to come up with a way of disabling language features like Math.random() and import() in worklets if you think that's really the best path forward.

@bfgeek
Copy link
Member Author

bfgeek commented Oct 11, 2017

We aren't going to stamp out all non-determinism, but I'm fine with leaving it as is and just saying "don't do that" for the time being. :)

@wanderview
Copy link
Member

Another reason treating these as subresource requests fits with our current model:

Currently the fetch and service worker spec expect non-subresource requests to target a single destination Client:

https://fetch.spec.whatwg.org/#concept-request-target-client-id
https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-target-browsing-context

Worklet scripts are loaded once, but may then be executed in multiple different Client contexts.

@nhiroki
Copy link

nhiroki commented Oct 11, 2017

Filed a Chromium issue to track this discussion: https://crbug.com/773778

@bfgeek
Copy link
Member Author

bfgeek commented Oct 25, 2017

@annevk @wanderview I've changed it to a sub-resource request, and rebased. (And I added the paintworklet and audioworklet destinations to be "script-like").

Copy link
Member

@annevk annevk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, thanks! Is there a testing story for these bits?

@annevk
Copy link
Member

annevk commented Oct 26, 2017

Also, to summarize the other issues that are somewhat related:

@bfgeek
Copy link
Member Author

bfgeek commented Oct 27, 2017

@nhiroki & I will work on some tests. Based on my understanding I think broadly we'll need.

  • A worklet.addModule('script.js') gets intercepted by a SW (and something that doesn't).
  • Any same-origin import statements also get intercepted.

It doesn't appear that anyone implements (and are no tests yet) for RequestDestination so we'll leave that alone for the time being.

@wanderview
Copy link
Member

Why wouldn't cross-origin import be intercepted? (This is different from import(), right?)

@bfgeek
Copy link
Member Author

bfgeek commented Oct 27, 2017

Sorry my bad, they should be...
Yeah this is different from the import() function.

@wanderview
Copy link
Member

wanderview commented Oct 30, 2017

As currently written import() would be invoked under an opaque origin. This means no CORS or service worker AFAICT.

I just want to follow-up here to clarify that I don't think imports() will work in a global with an opaque origin like worklets:

  • es modules require CORS (last I checked)
  • Sending a CORS requests requires setting an Origin header
  • A global with an opaque origin cannot set an Origin header (AFAIK)
  • All origins (including the owner document's) is considered cross-origin

So, at least in firefox, I do not expect imports() to function in worklets.

@domenic
Copy link
Member

domenic commented Oct 30, 2017

Note that static import and dynamic import() are the same in these regards, so if that's the case, the spec just doesn't allow importing libraries at all. I imagine the editors will want to fix that.

@wanderview
Copy link
Member

Note that static import and dynamic import() are the same in these regards, so if that's the case, the spec just doesn't allow importing libraries at all. I imagine the editors will want to fix that.

I don't think that's the case given the initial module graph is imported using the owner's context. We do have an origin in that case. The weirdness happens because the initial static import is performed under one origin and then the dynamic import() runs on a global explicitly set to an opaque origin.

@annevk
Copy link
Member

annevk commented Oct 31, 2017

A global with an opaque origin can have an Origin header (its value will be null).

@wanderview
Copy link
Member

A global with an opaque origin can have an Origin header (its value will be null).

Ah, you are correct. So it can do CORS to a server that allows *?

@annevk
Copy link
Member

annevk commented Nov 1, 2017

@wanderview yes. You can even do a request with credentials if the server replies with ACAO: null (and also sets the credentials header).

Copy link
Member

@annevk annevk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll go ahead and merge this given that I trust tests will be written for the service worker part, which is the most important bit here. And I also don't want this to go stale again.

@annevk annevk merged commit 8ab040a into whatwg:master Nov 1, 2017
@nhiroki
Copy link

nhiroki commented Nov 6, 2017

I'm now writing tests for service worker interception (crbug) and came across questions about dynamic import on WorkletGlobalScope.

  • Should we clarify in the Worklet spec that dynamic import() on WorkletGlobalScope must use the custom fetch algorithm like addModule() (see "To perform the fetch given request, perform the following steps:")? The current spec says nothing about dynamic import(). If needed, I'll file a spec issue in the Worklet repository.

  • What's the consensus of ServiceWorker interception for dynamic import()? Should it be handled by owner document's service worker or never handled because WorkletGlobalScope has an opaque origin? The latter option sounds reasonable if we consider dynamic import() as networking APIs like fetch(). On the other hand, it also sounds a bit odd because dynamic import() goes through the Worklet's module responses map logic shared with addModule() (static import).

@annevk
Copy link
Member

annevk commented Nov 6, 2017

@nhiroki I filed w3c/css-houdini-drafts#506.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

9 participants