Intent to Implement: Shadow DOM Plugin Placeholders

85 views
Skip to first unread message

Jeremy Roman

unread,
Nov 21, 2014, 10:28:38 AM11/21/14
to blink-dev, Bernhard Bauer, Elliott Sprehn, Mike West, Jochen Eisinger, Kentaro Hara
This is arguably not a formal Intent to Implement since it isn't a web platform feature per se -- and it's somewhat retroactive -- but I've been asked to adopt this template nonetheless since I intend to use Blink-in-JS in its implementation.

Contact emails

jbr...@chromium.org


Spec

Not applicable.


Summary

Chromium bug: http://crbug.com/364716

Design doc: http://goo.gl/x1jNfu


I intend to re-implement Chrome's plugin placeholders (e.g. those that are displayed when a plugin is not available, configured for click-to-play, etc.) using user agent shadow DOM in Blink. I would like to use Blink-in-JS to express the UI logic within these placeholders. This is strictly a browser/user-facing feature; authors only write an <object> tag or equivalent, and Chrome may choose to substitute a placeholder in cases where it decides that loading a plugin is not possible or not desirable.


Motivation

WebViewPlugin is one of the last remaining uses (another being the printing code) of the Chromium’s software path for graphics, which does not use either the software or hardware compositing mode. The compositor team would like to remove this legacy code path.


Shadow DOM is the natural way to embed (encapsulated) HTML content inside another HTML document on the web platform. By being in the same document, it benefits from improvements in the normal main-frame code (compositing, accessibility, animation, input handling, etc.) automatically. It is also open to inspection by DevTools, so authors can see how the UI is built with the technologies available to them.


Compatibility Risk

Low; I'm only proposing to replace our current implementation, and there is no exposed API surface. For what it's worth, Firefox seems to use their analogue to UA shadow DOM ("bindings") to implement their plugin placeholders.


Ongoing technical constraints

None.


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

Yes.


OWP launch tracking bug?

Not applicable.


Link to entry on the feature dashboard

Not applicable.


Requesting approval to ship?

No; this feature requires a Chromium-side flag to be set at present. I don't believe this requires API owner approval to ship once finished, though, since it does not affect the web platform API surface.


Hypothetically Asked Questions

1. How will the HTML, JS, CSS and images be loaded?

HTML elements will be dynamically created, ideally from JavaScript (but doing this from C++ is also possible).
JavaScript will be evaluated in the Blink-in-JS isolated world, though Blink-in-JS's private script runner.
CSS will be loaded into (and scoped to) the shadow root from a resource made available through a private resource scheme (CL in review). Dependent resources (e.g. images) can be loaded with relative URLs to this stylesheet.

2. What privileged capabilities does this require? (Alternatively, "what private script APIs are desired?")

To match the current implementation, two unusual capabilities are required:

(a) A way to continue loading a plugin, even when the user has configured it for click-to-play. 
- The simplest version of this would be a private (Blink-in-JS-only) "loadPlugin()" API on either the plugin element or the placeholder element. This is fairly simple and would not be web-exposed (nor would it be appropriate for it to be exposed, since it would allow authors to ignore the user's choice to not load potentially annoying or dangerous content by default).
- If the UI logic is in Blink-in-JS, an alternative way of invoking C++ might be emitting a private CustomEvent. Special C++ hooks (which already exist, but nonetheless...) would be needed to prevent this event from propagating outside the placeholder; they should not be visible even to capture-phase event listeners.
- If neither of these is acceptable, the DOM could be generated and controlled from C++, which would remove the need for a JS-to-C++ call here, but this complicates the code with lifetime management concerns.

(b) A way to open plugin settings (chrome://plugins/) on user request (currently shown in the "disabled plugin" placeholder). chrome:// URLs are privileged, and unprivileged renderers cannot ordinarily cause navigation to them.
- The current placeholders attempt to do this (though it doesn't seem to work right now) by sending ChromeViewHostMsg_OpenAboutPlugins to the browser process. A call into C++ (via a private binding or event, as above) would let Blink notify the embedder of this event; the embedder could then dispatch an IPC to the browser process similarly to today.
- A more webby way of triggering navigation would be to simply have a link. Currently there are two protections against this: First, Chromium (in the renderer) configures Blink to treat the chrome:// scheme as "display isolated", so Blink cancels any navigations to that scheme. Secondly, the browser process filters URLs according to the child's security policy (and this policy causes all chrome:// URLs to be rewritten to about:blank). It might be possible to poke holes in both of these measures to allow this navigation to occur, but this would require careful security consideration.

3. How will these placeholders react to changes that happen at the content layer or above (such as content settings changes)?

The plugin element (in Blink) will own a Chromium-side object that implements blink::WebPluginPlaceholder. This object may observe whatever events it is interested in from within content, just as the current (WebPlugin-backed) placeholders do. They can respond to these by updating their internal state, and by invoking methods on their container (which implements blink::WebPluginPlaceholderContainer) to cause the plugin to reload, or to cause placeholder content to refresh. The placeholder container will then cause the appropriate changes to occur (for instance, it might change the "closeable" attribute of the placeholder element to cause the close button to be hidden).

This is similar to the ownership/callback structure in WebPlugin and WebPluginContainer.

4. What is the history behind this design so far?

A summary of the evolution of the design doc and internal discussion is here: http://goo.gl/tw6Xiy

Jochen Eisinger

unread,
Nov 21, 2014, 11:30:45 AM11/21/14
to Jeremy Roman, blink-dev, Bernhard Bauer, Elliott Sprehn, Mike West, Kentaro Hara
Thanks for starting the thread.

How much javascript is there actually? I thought it's just listening to a click event, and implementing a context menu?

 
CSS will be loaded into (and scoped to) the shadow root from a resource made available through a private resource scheme (CL in review).

I'm concerned about adding a private URL scheme for this. I think we should add a regular API to get the code from chrome to blink.

 
Dependent resources (e.g. images) can be loaded with relative URLs to this stylesheet.


or encoded as data: urls
 
2. What privileged capabilities does this require? (Alternatively, "what private script APIs are desired?")

To match the current implementation, two unusual capabilities are required:

(a) A way to continue loading a plugin, even when the user has configured it for click-to-play. 
- The simplest version of this would be a private (Blink-in-JS-only) "loadPlugin()" API on either the plugin element or the placeholder element. This is fairly simple and would not be web-exposed (nor would it be appropriate for it to be exposed, since it would allow authors to ignore the user's choice to not load potentially annoying or dangerous content by default).
- If the UI logic is in Blink-in-JS, an alternative way of invoking C++ might be emitting a private CustomEvent. Special C++ hooks (which already exist, but nonetheless...) would be needed to prevent this event from propagating outside the placeholder; they should not be visible even to capture-phase event listeners.
- If neither of these is acceptable, the DOM could be generated and controlled from C++, which would remove the need for a JS-to-C++ call here, but this complicates the code with lifetime management concerns.

(b) A way to open plugin settings (chrome://plugins/) on user request (currently shown in the "disabled plugin" placeholder). chrome:// URLs are privileged, and unprivileged renderers cannot ordinarily cause navigation to them.
- The current placeholders attempt to do this (though it doesn't seem to work right now) by sending ChromeViewHostMsg_OpenAboutPlugins to the browser process. A call into C++ (via a private binding or event, as above) would let Blink notify the embedder of this event; the embedder could then dispatch an IPC to the browser process similarly to today.
- A more webby way of triggering navigation would be to simply have a link. Currently there are two protections against this: First, Chromium (in the renderer) configures Blink to treat the chrome:// scheme as "display isolated", so Blink cancels any navigations to that scheme. Secondly, the browser process filters URLs according to the child's security policy (and this policy causes all chrome:// URLs to be rewritten to about:blank). It might be possible to poke holes in both of these measures to allow this navigation to occur, but this would require careful security consideration.

I'd rather avoid having any hole where a regular renderer can navigate to chrome URLs.

Mike West

unread,
Nov 21, 2014, 11:44:52 AM11/21/14
to Jochen Eisinger, Jeremy Roman, blink-dev, Bernhard Bauer, Elliott Sprehn, Kentaro Hara
On Fri, Nov 21, 2014 at 5:30 PM, Jochen Eisinger <joc...@chromium.org> wrote:
I'm concerned about adding a private URL scheme for this. I think we should add a regular API to get the code from chrome to blink.

The advantage of a new scheme is that we can use the existing CSP/MIX-bypass mechanisms. Otherwise we'll have issues ensuring that the UI looks like it's supposed to when it's embedded on sites across the web.
 
Dependent resources (e.g. images) can be loaded with relative URLs to this stylesheet.


or encoded as data: urls

Which would require a new mechanism for bypassing CSP.

-mike

Jeremy Roman

unread,
Nov 21, 2014, 11:45:38 AM11/21/14
to Jochen Eisinger, blink-dev, Bernhard Bauer, Elliott Sprehn, Mike West, Kentaro Hara
That's most of it. I expect the most complicated piece to be the "hiding" logic, where I've ported the current heuristic of also hiding ancestors explicitly sized to match.
  
CSS will be loaded into (and scoped to) the shadow root from a resource made available through a private resource scheme (CL in review).

I'm concerned about adding a private URL scheme for this. I think we should add a regular API to get the code from chrome to blink.
 
Dependent resources (e.g. images) can be loaded with relative URLs to this stylesheet.

or encoded as data: urls

Data URLs do not load if there is a content security policy that does not include "data:". For instance, consider:

#plugin-placeholder-close-button {
    background-image: -webkit-image-set(
      url('default_100_percent/close_2.png') 1x,
      url('default_200_percent/close_2.png') 2x);
 }

#plugin-placeholder-close-button:hover {
  background-image: -webkit-image-set(
      url('default_100_percent/close_2_hover.png') 1x,
      url('default_200_percent/close_2_hover.png') 2x);
}

Handling this with a resource scheme is straightforward. Handling it with data URLs requires:
- preprocessing the stylesheet to insert data URLs (though on the Chrome side, there is WebUI infrastructure to handle this)
- somehow marking these data URLs as permitted to load, even though they may load in response to hover (at which point the main world V8 context is active etc.)

2. What privileged capabilities does this require? (Alternatively, "what private script APIs are desired?")

To match the current implementation, two unusual capabilities are required:

(a) A way to continue loading a plugin, even when the user has configured it for click-to-play. 
- The simplest version of this would be a private (Blink-in-JS-only) "loadPlugin()" API on either the plugin element or the placeholder element. This is fairly simple and would not be web-exposed (nor would it be appropriate for it to be exposed, since it would allow authors to ignore the user's choice to not load potentially annoying or dangerous content by default).
- If the UI logic is in Blink-in-JS, an alternative way of invoking C++ might be emitting a private CustomEvent. Special C++ hooks (which already exist, but nonetheless...) would be needed to prevent this event from propagating outside the placeholder; they should not be visible even to capture-phase event listeners.
- If neither of these is acceptable, the DOM could be generated and controlled from C++, which would remove the need for a JS-to-C++ call here, but this complicates the code with lifetime management concerns.

(b) A way to open plugin settings (chrome://plugins/) on user request (currently shown in the "disabled plugin" placeholder). chrome:// URLs are privileged, and unprivileged renderers cannot ordinarily cause navigation to them.
- The current placeholders attempt to do this (though it doesn't seem to work right now) by sending ChromeViewHostMsg_OpenAboutPlugins to the browser process. A call into C++ (via a private binding or event, as above) would let Blink notify the embedder of this event; the embedder could then dispatch an IPC to the browser process similarly to today.
- A more webby way of triggering navigation would be to simply have a link. Currently there are two protections against this: First, Chromium (in the renderer) configures Blink to treat the chrome:// scheme as "display isolated", so Blink cancels any navigations to that scheme. Secondly, the browser process filters URLs according to the child's security policy (and this policy causes all chrome:// URLs to be rewritten to about:blank). It might be possible to poke holes in both of these measures to allow this navigation to occur, but this would require careful security consideration.

I'd rather avoid having any hole where a regular renderer can navigate to chrome URLs.

Does this refer only to (b), or also to (a)? Regular renderers already do have permission to send ChromeViewHostMsg_OpenAboutPlugins to the browser. Using something similar to this was my initial plan; (b) describes what esprehn@'s comment on my CL (https://codereview.chromium.org/740063002/#msg13) would entail.

Jochen Eisinger

unread,
Nov 21, 2014, 2:18:46 PM11/21/14
to Mike West, Jeremy Roman, blink-dev, Bernhard Bauer, Elliott Sprehn, Kentaro Hara
On Fri Nov 21 2014 at 5:44:48 PM Mike West <mk...@chromium.org> wrote:
On Fri, Nov 21, 2014 at 5:30 PM, Jochen Eisinger <joc...@chromium.org> wrote:
I'm concerned about adding a private URL scheme for this. I think we should add a regular API to get the code from chrome to blink.

The advantage of a new scheme is that we can use the existing CSP/MIX-bypass mechanisms. Otherwise we'll have issues ensuring that the UI looks like it's supposed to when it's embedded on sites across the web.

Why are UA styles subject to CSP/MIX?

Jochen Eisinger

unread,
Nov 21, 2014, 2:22:09 PM11/21/14
to Jeremy Roman, blink-dev, Bernhard Bauer, Elliott Sprehn, Mike West, Kentaro Hara
btw, why does the plugin-not-found replacement work without this? I thought it also displays some form of image?
 

2. What privileged capabilities does this require? (Alternatively, "what private script APIs are desired?")

To match the current implementation, two unusual capabilities are required:

(a) A way to continue loading a plugin, even when the user has configured it for click-to-play. 
- The simplest version of this would be a private (Blink-in-JS-only) "loadPlugin()" API on either the plugin element or the placeholder element. This is fairly simple and would not be web-exposed (nor would it be appropriate for it to be exposed, since it would allow authors to ignore the user's choice to not load potentially annoying or dangerous content by default).
- If the UI logic is in Blink-in-JS, an alternative way of invoking C++ might be emitting a private CustomEvent. Special C++ hooks (which already exist, but nonetheless...) would be needed to prevent this event from propagating outside the placeholder; they should not be visible even to capture-phase event listeners.
- If neither of these is acceptable, the DOM could be generated and controlled from C++, which would remove the need for a JS-to-C++ call here, but this complicates the code with lifetime management concerns.

(b) A way to open plugin settings (chrome://plugins/) on user request (currently shown in the "disabled plugin" placeholder). chrome:// URLs are privileged, and unprivileged renderers cannot ordinarily cause navigation to them.
- The current placeholders attempt to do this (though it doesn't seem to work right now) by sending ChromeViewHostMsg_OpenAboutPlugins to the browser process. A call into C++ (via a private binding or event, as above) would let Blink notify the embedder of this event; the embedder could then dispatch an IPC to the browser process similarly to today.
- A more webby way of triggering navigation would be to simply have a link. Currently there are two protections against this: First, Chromium (in the renderer) configures Blink to treat the chrome:// scheme as "display isolated", so Blink cancels any navigations to that scheme. Secondly, the browser process filters URLs according to the child's security policy (and this policy causes all chrome:// URLs to be rewritten to about:blank). It might be possible to poke holes in both of these measures to allow this navigation to occur, but this would require careful security consideration.

I'd rather avoid having any hole where a regular renderer can navigate to chrome URLs.

Does this refer only to (b), or also to (a)? Regular renderers already do have permission to send ChromeViewHostMsg_OpenAboutPlugins to the browser. Using something similar to this was my initial plan; (b) describes what esprehn@'s comment on my CL (https://codereview.chromium.org/740063002/#msg13) would entail.

Just (b). In the end, the browser should be one that grants access to the plugin, no matter how much the renderer asks for it.

Jochen Eisinger

unread,
Dec 1, 2014, 3:31:02 AM12/1/14
to Jeremy Roman, blink-dev, Bernhard Bauer, Elliott Sprehn, Mike West, Kentaro Hara
pinging this thread again after the thanksgiving break...

Jeremy Roman

unread,
Dec 1, 2014, 10:22:57 AM12/1/14
to Jochen Eisinger, blink-dev, Bernhard Bauer, Elliott Sprehn, Mike West, Kentaro Hara
Sorry; I previously missed your actual question in amongst the quoted text. Answered inline.

Since I started this thread, I have learned that another subteam is actively developing a new plugin placeholder feature (naturally, on top of the current plugin placeholders). Since I haven't been able to make much forward progress on this bug -- and making only slow progress is likely to impede their development -- I've concluded that I need to suspend work on this. Since that leaves the code I have landed in indefinite limbo, I'm prepared to land reverts later this week (for what would become stagnant code that is dead in production and not in active development) and withdraw my intent to implement.

I'll update the bug status accordingly.

On Mon, Dec 1, 2014 at 3:30 AM, Jochen Eisinger <joc...@chromium.org> wrote:
pinging this thread again after the thanksgiving break...
On Fri Nov 21 2014 at 8:22:10 PM Jochen Eisinger <joc...@chromium.org> wrote:
On Fri Nov 21 2014 at 5:45:36 PM Jeremy Roman <jbr...@chromium.org> wrote:
On Fri, Nov 21, 2014 at 11:30 AM, Jochen Eisinger <joc...@chromium.org> wrote:
Thanks for starting the thread.

On Fri Nov 21 2014 at 4:28:36 PM Jeremy Roman <jbr...@chromium.org> wrote:
This is arguably not a formal Intent to Implement since it isn't a web platform feature per se -- and it's somewhat retroactive -- but I've been asked to adopt this template nonetheless since I intend to use Blink-in-JS in its implementation.

Contact emails

jbr...@chromium.org


Spec

Not applicable.


Summary


The current placeholders are loaded inside a WebView hosted by the in-process plugin; that WebView hosts a separate frame which is not bound by the CSP of its container.
Reply all
Reply to author
Forward
0 new messages