-
Notifications
You must be signed in to change notification settings - Fork 379
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
MSC2192: Inline widgets #2192
base: old_master
Are you sure you want to change the base?
MSC2192: Inline widgets #2192
Changes from all commits
560ef8c
ac18b27
65bfb26
998eec6
8632ddb
8557102
2a26597
d6124ca
a47a4a9
430bccd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
# MSC2192: Inline widgets | ||
|
||
Widgets are embedded applications that usually reside within the context of a room or | ||
account to add useful functionality, such as a collaborative whiteboard, stickers, | ||
dashboards, and conferencing. Currently, this feature doesn't extend into the timeline | ||
itself for rich embeds of content, like videos and other sharable content. | ||
|
||
This MSC proposes "inline widgets" as a mechanism for sharing embeddable content within | ||
a room, primarily intended to cover video (YouTube, etc) embeds but able to cover a | ||
wide range of use cases. | ||
|
||
To achieve this, we use [MSC1767](https://github.com/matrix-org/matrix-doc/pull/1767) to | ||
define a new event type and suitable "content blocks" for supporting widgets and embeds | ||
within a room. | ||
|
||
For reference, the original Widget API is defined as [MSC1236](https://github.com/matrix-org/matrix-doc/issues/1236). | ||
|
||
## Proposal | ||
|
||
Using [MSC1767](https://github.com/matrix-org/matrix-doc/pull/1767)'s system, we define | ||
an `m.embed` event type with suitable content blocks to cover a web-based embedded | ||
application in the timeline. | ||
|
||
An example is: | ||
|
||
```json5 | ||
{ | ||
"type": "m.embed", | ||
"content": { | ||
"m.text": [ | ||
// Format of the fallback is not defined, but should have enough information for a text-only | ||
// client to do something with the widget. | ||
{"body": "https://www.youtube.com/watch?v=Vn-NZvMcujc"} | ||
], | ||
"m.widget": { | ||
"url": "https://www.youtube.com/embed/Vn-NZvMcujc", | ||
"waitForIframeLoad": true, | ||
"type": " | ||
} | ||
} | ||
} | ||
``` | ||
|
||
With consideration for extensible events, the following content blocks are defined: | ||
|
||
* `m.widget` - The same fields as a widget event in room state, minus `creatorUserId` | ||
and `id`. Instead, implementations should use the `sender` as the creator and `event_id` | ||
as widget ID. | ||
|
||
Together with content blocks from other proposals, an `m.embed` is described as: | ||
|
||
* **Required** - An `m.text` block to act as a fallback for clients which can't process | ||
inline widgets. | ||
* **Required** - An `m.widget` block to describe the widget itself. Clients use this to show | ||
the widget. | ||
|
||
The above describes the minimum requirements for sending an `m.embed` event. Senders can add | ||
additional blocks, however as per the extensible events system, receivers which understand | ||
inline widget events should not honour them. | ||
|
||
If a client does not support rendering inline widgets, the client would instead typically | ||
represent the event as a plain text message. | ||
|
||
When rendering the inline widget's iframe, all the normal widget options apply, including | ||
availability of the Widget API. Further, templating on the URL is also supported, and | ||
widgets should still *not* rely on `$matrix_user_id` being trusted. | ||
|
||
Clients SHOULD limit the maximum height and width of inline widgets to prevent | ||
large portions of the timeline being inaccessible to users. Scrollbars are encouraged | ||
for long/large content. | ||
|
||
## Potential issues | ||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Previous drafts of this proposal supported an extended set of Matrix HTML to offer forms to users | ||
which could then be used to render polls and similar functionality without the integration | ||
asking the question requiring a web server. The complexity with that approach was that | ||
it relied on HTML, which some clients cannot or will not support. The approach additionally | ||
opened up the client to several XSS and similar security vulnerabilities due to the complex | ||
nature of untrusted user-provided HTML being rendered in the client. | ||
|
||
## Security considerations | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be feasible to add a power_level event for sending There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be worth mentioning the |
||
|
||
Allowing arbitrary embeds opens up a spam vector for auto-playing videos, scare content, | ||
and similar spam. Clients should do their best to avoid these kinds of attacks, such as | ||
by blocking the widget from loading until the user accepts the widget or otherwise has | ||
trust in the sender (where "trust" is left as an implementation detail). | ||
|
||
## Unstable prefix | ||
|
||
While this MSC is not considered stable, implementations should use `org.matrix.msc2192.` | ||
in place of `m.`. For example, `m.widget` becomes `org.matrix.msc2192.widget` | ||
|
||
## Alternative solutions | ||
|
||
There are some possible alternatives: | ||
|
||
#### Extended Matrix HTML for inline widgets | ||
|
||
As alluded to earlier in this proposal, inline HTML could be used instead of the | ||
`widget_url` field. In the interest of preserving the historical record more cleanly, | ||
the entire previous draft is included here. | ||
|
||
An inline HTML widget would take the following form, noting the use of `widget_html` | ||
instead of `widget_url` to define the widget: | ||
```json | ||
{ | ||
"type": "m.room.message", | ||
"content": { | ||
"msgtype": "m.widget", | ||
"body": "Poll: What is your favourite food?", | ||
"widget_html": "<button data-mx-action='sendText' data-mx-value='Ice Cream'>Ice Cream</button><button data-mx-action='sendText' data-mx-value='Pizza'>Pizza</button>", | ||
"waitForIframeLoad": true, | ||
"type": "m.custom", | ||
"name": "Poll", | ||
"data": { | ||
"title": "Favourite Food" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Events should only contain a `widget_url` or a `widget_html`, however when both are | ||
specified clients should prefer to use the `widget_url`. | ||
|
||
Inline HTML widgets inherit all behaviour of inline widgets above. | ||
|
||
The HTML supported by `widget_html` is the same HTML set supported by Matrix | ||
already (see paramgraphs under [m.room.message types](https://matrix.org/docs/spec/client_server/r0.5.0#m-room-message-msgtypes)) | ||
with a few additions: | ||
* `button` is allowed with the `data-mx-action`,`data-mx-value`, and `name` attributes. | ||
* `select` is allowed with the `name` attribute. | ||
* `option` (under `select`) is allowed with the `value` and `selected` attributes. | ||
* `input` is allowed with types `radio`, `checkbox`, `text`, and `password`. Inputs | ||
can additionally have `placeholder`, `value`, `checked`, and `name` as attributes. | ||
* `form` is allowed with the `data-mx-event-type` attribute. | ||
* `label` is allowed. | ||
|
||
The attributes have special meanings: | ||
* `data-mx-action` can be one of `m.send_text`, `m.send_notice`, or `m.send_hidden`. | ||
Because the attribute is only applicable to buttons, when the button is clicked | ||
the action occurs through the Widget postMessage API: | ||
* `m.send_text`: Sends the `data-mx-value` as an `m.text` room message. | ||
* `m.send_notice`: Sends the `data-mx-value` as an `m.notice` room message. | ||
* `m.send_hidden`: Sends the `data-mx-value` as an `m.room.hidden` event in the | ||
`body` field of the `content`. | ||
|
||
When buttons do not have this field, they should be assumed to be submit buttons | ||
for the parent `form`, if present. | ||
* `data-mx-event-type` is the type of event to send when the form is submitted. The | ||
event's content will be a mapping of all the `name` attributes for form elements | ||
(`select` and `input`) in the form, with the added `m.form_name` for the `form`'s | ||
`name` and `m.button_name` for the `button`'s `name` which was clicked. For example, | ||
the following HTML: | ||
```html | ||
<form data-mx-event-type="org.example.poll_response" name="favourite_food"> | ||
<select name="preset"> | ||
<option value="Ice Cream" selected>Ice Cream</option> | ||
<option value="Pizza">Pizza</option> | ||
</select> | ||
<input type="text" placeholder="Other" name="other"> | ||
<label> | ||
<input type="checkbox" name="certified" value="yes" checked> | ||
I certify this is my favourite food. | ||
</label> | ||
|
||
<p> | ||
Second favourite food: | ||
<label><input type="radio" name="second_favourite" value="Ice Cream" checked> Ice Cream</label> | ||
<label><input type="radio" name="second_favourite" value="Pizza"> Pizza</label> | ||
<label><input type="radio" name="second_favourite" value="Chips"> Chips</label> | ||
</p> | ||
<button name="submit">Submit</button> | ||
</form> | ||
``` | ||
would produce the following event when the submit button was pressed: | ||
```json | ||
{ | ||
"type": "org.example.poll_response", | ||
"content": { | ||
"m.form_name": "favourite_food", | ||
"m.button_name": "submit", | ||
"preset": "Ice Cream", | ||
"other": "Doughnuts", | ||
"certified": "yes", | ||
"second_favourite": "Ice Cream" | ||
} | ||
} | ||
``` | ||
|
||
If no form `name` is specified, `m.form_name` is not included. Likewise for the | ||
button pressed. `m.form_name` and `m.button_name` SHOULD be added last to avoid | ||
forms overwriting the values (although doing so wouldn't cause problems anyways). | ||
|
||
For radio buttons, checkboxes, and selects: if no option is selected then the | ||
value is not included in the event. Empty textboxes are included as empty strings. | ||
|
||
Note that no validation is permitted by the HTML: the use of `widget_html` is | ||
not meant to be fully-featured. More advanced use cases are expected to be | ||
handled by `widget_url` instead, as it is not under the same limitations. | ||
|
||
When handling `widget_html`, clients SHOULD wrap the HTML in their own local | ||
wrapper. For example, a client may embed an iframe with a URL of | ||
`/widget_html.html?html=...` for sandboxing. | ||
|
||
In order to allow the widget to send custom/non-standardized events, a new postMessage | ||
API needs to be considered. In MSC1236 there is a `fromWidget` action for `send_event`, | ||
however in practice this action was never implemented. Instead, the action was | ||
implemented as the event type to send (`fromWidget` action of `m.sticker`, for example). | ||
This proposal re-introduces `send_event` with an altered payload to better suit custom | ||
events: | ||
```json | ||
{ | ||
"api": "fromWidget", | ||
"action": "send_event", | ||
"widgetId": "ID_GOES_HERE", | ||
"event_type": "org.example.poll_response", | ||
"event_content": {...} | ||
} | ||
``` | ||
|
||
This proposal does not support sending state events through this action. To negotiate | ||
sending of the event type, the widget would request the event type prefixed with the | ||
string `"m.send."` as the capability. For example, the widget would request the | ||
`m.send.org.example.poll_response` capability in the above examples. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potentially this could also be displayed as a caption so users can explain why someone would want to approve the widget opening in their client? I'd definitely feel more comfortable being prompted before loading external content that may identify my location/machine/etc.