├── .pr-preview.json ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── demos ├── images │ ├── README.md │ ├── favicon.ico │ ├── icon-128.png │ ├── icon-16.png │ ├── icon-180.png │ ├── icon-256.png │ ├── icon-32.png │ ├── icon-48.png │ └── icon-512.png ├── index.html ├── manifest.json ├── sharetarget.html └── sw.js ├── docs ├── explainer.md ├── interface.md ├── mocks │ ├── README.md │ ├── share_mobile_handler.png │ └── share_mobile_web_web.png └── native.md ├── index.html ├── level-2 └── index.html ├── tidyconfig.txt └── w3c.json /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "level-2/index.html", 3 | "type": "respec" 4 | } 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Everyone is welcome to contribute to this specification. 4 | 5 | Any simple editorial contribution can simply be done with a GitHub Pull Request. 6 | You can even do an inline edit of the file on GitHub. 7 | 8 | For more substantial contributions, please first start a thread in the 9 | [webapps mailing list](https://lists.w3.org/Archives/Public/public-webapps/) at the W3C. 10 | 11 | Note: Contributions to this repository are intended to become part of Recommendation-track documents governed by the 12 | [W3C Patent Policy](https://www.w3.org/Consortium/Patent-Policy-20040205/) and 13 | [Software and Document License](https://www.w3.org/Consortium/Legal/copyright-software). To make substantive contributions to specifications, you must either participate 14 | in the relevant W3C Working Group or make a non-member patent licensing commitment. 15 | 16 | If you are not the sole contributor to a contribution (pull request), please identify all 17 | contributors in the pull request comment. 18 | 19 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 20 | 21 | ``` 22 | +@github_username 23 | ``` 24 | 25 | If you added a contributor by mistake, you can remove them in a comment with: 26 | 27 | ``` 28 | -@github_username 29 | ``` 30 | 31 | If you are making a pull request on behalf of someone else but you had no part in designing the 32 | feature, you can remove yourself with the above syntax. 33 | 34 | # Style guide to contributors 35 | 36 | - the spec uses [ReSpec](https://www.w3.org/respec/) 37 | - the spec is tidied using [HTML5 Tidy](https://github.com/w3c/tidy-html5). For 38 | instructions on running HTML5 tidy, see below. 39 | - put comments in front of sections, for better readability with 40 | syntax coloring editors. 41 | 42 | # Running HTML5 Tidy 43 | 44 | Please make sure you have HTML5 tidy installed, instead of 45 | the the one that ships with \*nix systems. You can comfirm this by running: 46 | 47 | ```bash 48 | tidy --version #HTML Tidy for HTML5 (experimental) for ... 49 | ``` 50 | 51 | Once you have confirmed (make sure you have committed your changes before 52 | running tidy, as the changes are destructive ... in a good way:)): 53 | 54 | ```bash 55 | tidy -config tidyconfig.txt -o index.html index.html 56 | ``` 57 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All documents in this Repository are licensed by contributors 2 | under the [W3C Software and Document License](https://www.w3.org/Consortium/Legal/copyright-software). 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web Share Target API 2 | 3 | **Date**: 2016-06-08 4 | 5 | This site details a proposal to add a capability for websites to receive shared 6 | data from other sites and apps. 7 | 8 | * [Explainer document](docs/explainer.md), a high-level overview of the proposal. 9 | * [Specification - Level 1](https://w3c.github.io/web-share-target/). 10 | * [Specification - Level 2](https://w3c.github.io/web-share-target/level-2/). 11 | * [Native integration survey](docs/native.md), for platform-specific matters. 12 | 13 | This is a product of the [Ballista 14 | project](https://github.com/chromium/ballista), which aims to explore 15 | website-to-website and website-to-native interoperability. 16 | 17 | 18 | ## Licensing and contributions 19 | 20 | See [LICENSE](LICENSE.md) and [CONTRIBUTING](CONTRIBUTING.md). 21 | -------------------------------------------------------------------------------- /demos/images/README.md: -------------------------------------------------------------------------------- 1 | Icon source: [pixabay / 2 | ArtsyBee](https://pixabay.com/en/target-goal-business-icon-logo-1151287/) 3 | (Creative Commons CC0) 4 | -------------------------------------------------------------------------------- /demos/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c/web-share-target/72dd80f90e24269804dd2ab6e9413eeb42935c5c/demos/images/favicon.ico -------------------------------------------------------------------------------- /demos/images/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c/web-share-target/72dd80f90e24269804dd2ab6e9413eeb42935c5c/demos/images/icon-128.png -------------------------------------------------------------------------------- /demos/images/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c/web-share-target/72dd80f90e24269804dd2ab6e9413eeb42935c5c/demos/images/icon-16.png -------------------------------------------------------------------------------- /demos/images/icon-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c/web-share-target/72dd80f90e24269804dd2ab6e9413eeb42935c5c/demos/images/icon-180.png -------------------------------------------------------------------------------- /demos/images/icon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c/web-share-target/72dd80f90e24269804dd2ab6e9413eeb42935c5c/demos/images/icon-256.png -------------------------------------------------------------------------------- /demos/images/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c/web-share-target/72dd80f90e24269804dd2ab6e9413eeb42935c5c/demos/images/icon-32.png -------------------------------------------------------------------------------- /demos/images/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c/web-share-target/72dd80f90e24269804dd2ab6e9413eeb42935c5c/demos/images/icon-48.png -------------------------------------------------------------------------------- /demos/images/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c/web-share-target/72dd80f90e24269804dd2ab6e9413eeb42935c5c/demos/images/icon-512.png -------------------------------------------------------------------------------- /demos/index.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | Web Share Target Demos 8 | 9 | 10 |

Web Share Target Demos

11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demos/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Web Share Target Test App", 3 | "short_name": "Target Test", 4 | "start_url": "sharetarget.html", 5 | "display": "standalone", 6 | "share_target": { 7 | "url_template": "sharetarget.html?oldapi=true&title={title}&text={text}&url={url}", 8 | "action": "sharetarget.html", 9 | "params": { 10 | "title": "title", 11 | "text": "text", 12 | "url": "url" 13 | } 14 | }, 15 | "icons": [{ 16 | "src": "images/icon-16.png", 17 | "sizes": "16x16" 18 | }, { 19 | "src": "images/icon-32.png", 20 | "sizes": "32x32" 21 | }, { 22 | "src": "images/icon-48.png", 23 | "sizes": "48x48" 24 | }, { 25 | "src": "images/icon-128.png", 26 | "sizes": "128x128" 27 | }, { 28 | "src": "images/icon-180.png", 29 | "sizes": "180x180" 30 | }, { 31 | "src": "images/icon-256.png", 32 | "sizes": "256x256" 33 | }, { 34 | "src": "images/icon-512.png", 35 | "sizes": "512x512" 36 | }], 37 | "theme_color": "#111111", 38 | "background_color": "#ffffff" 39 | } 40 | -------------------------------------------------------------------------------- /demos/sharetarget.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | Web Share Target Test App 8 | 9 | 10 | 11 | 16 | 17 | 18 |

Web Share Target Test App

19 |
20 |

This is a test app for the Web 21 | Share Target API. It will only work in browsers that have implemented the 22 | draft proposal. At the time of writing, this works with:

23 | 49 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /demos/sw.js: -------------------------------------------------------------------------------- 1 | /* This work is licensed under the W3C Software and Document License 2 | * (http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). 3 | */ 4 | 5 | self.addEventListener('fetch', event => { 6 | event.respondWith((async () => { 7 | // Get content from the network. 8 | try { 9 | return await fetch(event.request); 10 | } catch (e) { 11 | // Failure. Just return a 200 page, to satisfy Lighthouse. 12 | return new Response('You are offline :(', {status: 200}); 13 | } 14 | })()); 15 | }); 16 | -------------------------------------------------------------------------------- /docs/explainer.md: -------------------------------------------------------------------------------- 1 | # Web Share Target API Explained 2 | 3 | **Date**: 2016-06-01 4 | 5 | **Web Share Target** is a proposed web API to enable a web site to receive 6 | shared data from other sites or apps. Many modern operating systems have a 7 | "share" concept, where the user gets to pick an app to receive the shared data. 8 | The goal is to allow web apps to appear in the UI for picking an app to share 9 | to. 10 | 11 | In combination with the [Web Share API](https://github.com/w3c/web-share) 12 | (being proposed in parallel), this would allow cross-sharing between websites on 13 | any platform (even those without a native sharing system). 14 | 15 | This is a product of the [Ballista 16 | project](https://github.com/chromium/ballista), which aims to explore 17 | website-to-website and website-to-native interoperability. 18 | 19 | See also: 20 | * [Specification](https://w3c.github.io/web-share-target/). 21 | * [Native integration survey](native.md), for platform-specific matters. 22 | 23 | ## User flow 24 | 25 | ### Registering a website as a handler on mobile 26 | 27 | Here's how a user can register one of their favourite websites to receive share 28 | actions from other websites and native apps. These mocks look a bit like 29 | Android, but we're designing with general desktop and mobile operating systems 30 | in mind. 31 | 32 | ![Share on mobile: handler registration](mocks/share_mobile_handler.png) 33 | 34 | [[Image credits](mocks/README.md)] 35 | 36 | 1. User visits a social networking website. The site has an `"share_target"` section 37 | in its [web manifest](https://w3c.github.io/manifest/), declaratively 38 | specifying that it can receive share actions (see [code](#sample-code)). 39 | 2. User indicates to the browser that they wish to register the site. **For 40 | discussion:** We have not determined whether this should be a) something the 41 | site can trigger programmatically through a JavaScript API, b) something the 42 | user must trigger through the browser UI, or c) something the browser 43 | automatically prompts for in response to some stimulus (e.g., user visiting 44 | the site many times), or a combination of the above. At the moment, we are 45 | assuming there is no API and registration is at the discretion of the 46 | browser/user. In Chrome, we envision the "Add to Home screen" button on the 47 | browser drop-down menu will provide an adequate signal to register the 48 | handler. 49 | 3. The user confirms registration of the site for the purpose of "Shared links" 50 | (this string is tailored by the browser specifically for each verb the 51 | handler is requesting). 52 | 53 | The site now shows up in share pickers. What that means depends on the 54 | underlying system, as explored in the next section. 55 | 56 | ### Sharing from web to web on mobile 57 | 58 | Here we see the interaction between the [Web Share 59 | API](https://github.com/w3c/web-share) and the Share Target API. You could 60 | also share from a native app (depending on the system). 61 | 62 | ![Share on mobile: web to web](mocks/share_mobile_web_web.png) 63 | 64 | 1. The user clicks "share" from a web page. 65 | 2. The intent picker is shown. "Example Social" appears in the list of 66 | applications, alongside native apps. Here, it is shown above a horizontal 67 | line, which is how it would appear in Android 6.0+ using the [Direct 68 | Share](http://developer.android.com/about/versions/marshmallow/android-6.0.html#direct-share) 69 | feature to dynamically insert handlers into the system intent picker. The 70 | user picks "Example Social". 71 | 3. The Example Social web page opens in a new browser tab. It is pre-populated 72 | (via an event being delivered to the page's service worker; see 73 | [code](#sample-code)) with the Subject and Message in the post text field. 74 | 75 | This flow will be different depending on the capabilities of the operating 76 | system. There are three broad approaches possible in descending order of 77 | preference: 78 | 79 | 1. Handlers are inserted into the system share picker dialog, as shown above. 80 | This allows share events coming from native applications to be delivered to 81 | web applications via the same mechanism. This approach should be possible on 82 | Android 6.0 (M) and above. 83 | 2. The browser presents its own picker UI with all web handlers, as well as a 84 | "Share to system" button. Clicking "Share to system" triggers the system 85 | share picker dialog. This approach does not allow for native-to-web sharing, 86 | and also presents additional friction for web-to-native sharing. 87 | 3. The browser presents its own picker UI with all web handlers, and no way to 88 | share to native apps. This approach would be taken on platforms with no 89 | native sharing infrastructure (desktop operating systems). 90 | 91 | **Potential pitfall**: With approach #1, we may need a way to filter out what 92 | the user will perceive as duplicate entries: a native app and web app of the 93 | same thing (e.g., Facebook), or the same web app registered with two browsers. 94 | At least on Android, the web apps will be badged with the browser's icon. 95 | 96 | For more technical details on integrating with native apps, see [Native 97 | Integration Story](native.md). 98 | 99 | Users may wish to share photos and other files, not just links. We aim 100 | to support this in the future, with web share targets able to specify 101 | in their manifests which MIME types they accept. 102 | 103 | ## Sample code 104 | 105 | Here's how to register a website to appear in the list of apps that can handle a 106 | "share" intent on Android, or a "share" action from another website. 107 | 108 | You need a [web app manifest](https://w3c.github.io/manifest/), to notify the 109 | browser of your ability to handle a share. 110 | 111 | #### manifest.webmanifest 112 | 113 | ```JSON 114 | { 115 | "name": "Example Social", 116 | "short_name": "Example Social", 117 | "icons": [...], 118 | "share_target": { 119 | "action": "share.html", 120 | "params": { 121 | "title": "name", 122 | "text": "description", 123 | "url": "link" 124 | } 125 | } 126 | } 127 | ``` 128 | 129 | Note: the action should be relative to the manifest URL, since the action is 130 | appended to the manifest URL (excluding the filename of the -manifest). 131 | 132 | #### share.html 133 | 134 | ```html 135 | 136 | 137 | 138 | 139 | 140 | 150 | 151 | 152 | ``` 153 | 154 | ## Frequently Asked Questions 155 | 156 | ### Why is this needed? 157 | 158 | This is one of the missing pieces of letting web apps behave like native apps. 159 | We've recently gotten new web technologies to let websites be installed 160 | alongside native apps, run outside of the browser window, theme the system title 161 | bars, and receive push notifications. The one thing many web apps (chat clients, 162 | social networking apps) need is a way to receive content from other web apps and 163 | native apps. 164 | 165 | ### Why not use registerProtocolHandler? 166 | 167 | This is discussed at length in the [Web Share 168 | Explainer](https://github.com/w3c/web-share/blob/master/docs/explainer.md#user-content-why-not-make-a-share-uri-scheme-like-mailto-instead-of-a-javascript-api). 169 | If we used a URI scheme for Share (instead of an API), then it would make sense 170 | to use the existing 171 | [registerProtocolHandler](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler) 172 | API to let websites receive shares. 173 | 174 | However, there are a number of downsides to this approach, discussed in the 175 | other document. 176 | -------------------------------------------------------------------------------- /docs/interface.md: -------------------------------------------------------------------------------- 1 | # Web Share Target API Interface 2 | 3 | See [Specification](https://w3c.github.io/web-share-target/). 4 | -------------------------------------------------------------------------------- /docs/mocks/README.md: -------------------------------------------------------------------------------- 1 | The mocks in this directory contain images licensed as follows: 2 | 3 | * First cat picture: CC0 Public Domain. 4 | [Source](https://pixabay.com/en/animal-cat-drawing-feline-kitten-1296305/): 5 | OpenClipartVectors 6 | * Second cat picture: Creative Commons Attribution 3.0. 7 | [Source](http://xxspiritwolf2000xx.deviantart.com/art/Grumpy-cat-line-art-394007711): 8 | XXspiritwolf2000XX 9 | -------------------------------------------------------------------------------- /docs/mocks/share_mobile_handler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c/web-share-target/72dd80f90e24269804dd2ab6e9413eeb42935c5c/docs/mocks/share_mobile_handler.png -------------------------------------------------------------------------------- /docs/mocks/share_mobile_web_web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c/web-share-target/72dd80f90e24269804dd2ab6e9413eeb42935c5c/docs/mocks/share_mobile_web_web.png -------------------------------------------------------------------------------- /docs/native.md: -------------------------------------------------------------------------------- 1 | # Web Share Target API: Native Integration Survey 2 | 3 | **Date**: 2016-06-06 4 | 5 | This document is an informal and incomplete survey of various operating systems' 6 | share systems, for exploring how a user agent might automatically map the [Share 7 | Target API](explainer.md) into the native system. See also: [Share API Native 8 | Integration 9 | Survey](https://github.com/w3c/web-share/blob/master/docs/native.md). 10 | 11 | *Note:* I (mgiuca@chromium.org) am not very familiar with these details. I 12 | gathered this information from reading the online documentation and 13 | experimenting with apps, and have not had experience programming against these 14 | APIs. I would appreciate being informed of any errors. 15 | 16 | ## Android 17 | 18 | * Apps can send *intents* to the system which can be delivered to an app of the 19 | user's choice. 20 | * Intent targets can be added dynamically on Android M+ using the [Direct Share 21 | API](http://developer.android.com/about/versions/marshmallow/android-6.0.html#direct-share). 22 | (This is what allows the Messenger app to add individual contacts as share 23 | intent targets.) 24 | This allows the user agent to add websites to the system intent picker (to 25 | receive intents from native apps). The intent would be delivered to the user 26 | agent's app, which would then figure out the appropriate web handler, marshal 27 | the intent data into a web object, and pass it to the event handler. 28 | * There may be a limit on the number of sites that can be added this way. 29 | 30 | ## iOS 31 | 32 | * Apps can send a share event to the system. The dialog for picking a share 33 | receiver is the "Share sheet". 34 | * In order to have an app appear in the Share sheet, you need to create an [app 35 | extension](https://developer.apple.com/app-extensions/). There is no way to 36 | dynamically create app extensions (extensions are created as [Xcode 37 | targets](https://developer.apple.com/library/ios/documentation/General/Conceptual/ExtensibilityPG/index.html) 38 | and reviewed by the [App Store review 39 | process](https://developer.apple.com/app-store/review/guidelines/#extensions)). 40 | The best that a user agent can probably do here is create a single share 41 | target for the whole agent (e.g., "Websites") and then if content is shared to 42 | that target, present a secondary picker of registered web handlers. 43 | 44 | ## Windows (Universal Windows Platform) 45 | 46 | * [Universal Windows Platform 47 | (UWP)](https://msdn.microsoft.com/en-us/windows/uwp/get-started/whats-a-uwp) 48 | is supported on Windows 10 mobile, desktop, Xbox, etc. 49 | * UWP APIs are not available to normal Win32 .exe applications. This will only 50 | be available for user agents that are UWP apps (e.g., Microsoft Edge is, 51 | Google Chrome isn't). 52 | * Windows 8, 8.1 have similar share APIs but unclear whether compatible with 53 | the Windows 10 ones. 54 | * UWP apps can initiate a "Share contract" to share to another UWP app. UX: 55 | Shows a modal share target picker on the right hand side of the screen (at 56 | least on desktop Windows 10). 57 | * [Receive 58 | API](https://msdn.microsoft.com/en-us/windows/uwp/app-to-app/receive-data): 59 | Registration is in the Windows [app package 60 | manifest](https://msdn.microsoft.com/en-au/library/windows/apps/br211474.aspx). 61 | It is unlikely that you could dynamically register web handlers into this list 62 | (the same problem and work-around as with iOS). 63 | 64 | ## Others 65 | 66 | * No known native share mechanism on Windows (<=7), Mac, Linux (Desktop), Chrome 67 | OS. 68 | 69 | ## Summary 70 | 71 | Unlike the web-to-native sharing (in the [Share 72 | API](https://github.com/w3c/web-share), which is generally pretty 73 | straightforward to route into a system share, native-to-web is usually hard or 74 | impossible for one reason: operating systems don't like letting one application 75 | dynamically register multiple handlers for things (with the notable exception of 76 | Android M+). There are usually work-arounds but it is hard to make web 77 | applications receive actions like first class apps. 78 | 79 | On these platforms, implementations have two non-ideal options: 80 | 81 | 1. Declare the browser itself as a system share target, then have users pick the 82 | browser, and display a sub-picker within the browser for web apps, or 83 | 2. Only support web-to-web sharing (showing a picker within the browser when a 84 | website uses the [Share API](https://github.com/w3c/web-share)). 85 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Web Share Target API 7 | 8 | 10 | 34 | 35 | 36 |
37 |

38 | This specification defines an API that allows websites to declare 39 | themselves as web share targets, which can receive shared 40 | content from either the [[[Web-Share]]], or system events (e.g., shares 41 | from native apps). 42 |

43 |

44 | This is a similar mechanism to 45 | {{NavigatorContentUtils/registerProtocolHandler()}}, in that it works 46 | by registering the website with the user agent, to later be 47 | invoked from another site or native application via the user 48 | agent (possibly at the discretion of the user). The difference is that 49 | {{NavigatorContentUtils/registerProtocolHandler()}} registers the 50 | handler via a programmatic API, whereas a Web Share Target is declared 51 | in the [[[appmanifest]]], to be registered at a time of the user agent 52 | or user's choosing. 53 |

54 |
55 |
56 |

57 | This is an early draft of the Web Share Target spec. 58 |

59 |
60 |
61 |

62 | Prerequisites 63 |

64 |

65 | In order to implement this API, a the user agent MUST support 66 | [[[appmanifest]]]. This spec also re-uses some definitions from the 67 | [[[Web-Share]]] spec. However, support for the [[[Web-Share]]] is 68 | OPTIONAL. 69 |

70 |
71 |
72 |

73 | Usage Example 74 |

75 |

76 | To register a site as a share target, a [=manifest/share_target=] entry 77 | is added to the [[[appmanifest]]], as shown: 78 |

79 |
 80 |       {
 81 |         "name": "Includinator",
 82 |         "share_target": {
 83 |           "action": "share.html",
 84 |           "params": {
 85 |             "title": "name",
 86 |             "text": "description",
 87 |             "url": "link"
 88 |           }
 89 |         }
 90 |       }
 91 |       
92 |

93 | The [=ShareTarget/params=] keys correspond to the key names in 94 | {{ShareData}} from [[[Web-Share]]], while the values are arbitrary 95 | names that will be used as query parameters when the target is 96 | launched. 97 |

98 |

99 | When a share takes place, if the user selects this share target, the 100 | user agent opens a new browsing context at the `action` URL, with query 101 | parameter values containing the shared data, just like an HTML form 102 | submission. 103 |

104 |

105 | For the purpose of this example, we assume the manifest is located at 106 | `https://example.org/includinator/manifest.webmanifest`. 107 |

108 |
109 |       <html>
110 |       <link rel="manifest" href="manifest.webmanifest">
111 |       <script>
112 |         window.addEventListener('load', () => {
113 |           const parsedUrl = new URL(window.location);
114 |           const { searchParams } = parsedUrl;
115 |           console.log("Title shared:", searchParams.get('name'));
116 |           console.log("Text shared:", searchParams.get('description'));
117 |           console.log("URL shared:", searchParams.get('link'));
118 |         });
119 |       </script>
120 |       
121 |

122 | If an incoming share contains the title "My News" and the URL 123 | `http://example.com/news`, the user agent will open a new window or tab 124 | and navigate to: 125 |

126 |
127 | https://example.org/includinator/share.html?name=My+News&link=http%3A%2F%2Fexample.com%2Fnews
128 | 
129 |

130 | U+0020 (SPACE) characters are encoded as "`+`", due to the use of 131 | [=`application\/x-www-form-urlencoded`=] encoding, not "`%20`" as might 132 | be expected. Processors must take care to decode U+002B (+) characters 133 | as U+0020 (SPACE), which some URL decoding libraries, including 134 | ECMAScript's `decodeURIComponent` 136 | function, may not do automatically. 137 |

138 |

139 | The query parameters are populated with information from the 140 | {{ShareData}} being shared. If the {{ShareData}} contains no 141 | information for a given member, the query parameter is omitted. 142 |

143 |

144 | A share target might only be interested in a subset of the 145 | {{ShareData}} members. This example also shows a share target that 146 | receives data as a `POST` request, which should be the case if the 147 | request causes an immediate side effect. 148 |

149 |
150 |       {
151 |         "name": "Bookmark",
152 |         "share_target": {
153 |           "action": "/bookmark",
154 |           "method": "POST",
155 |           "enctype": "multipart/form-data",
156 |           "params": {
157 |             "url": "link"
158 |           }
159 |         }
160 |       }
161 |       
162 |

163 | The shared information might be read by a [=service worker=], rather 164 | than being sent over the network to the server. 165 |

166 |
167 |         self.addEventListener("fetch", (event) => {
168 |           if (event.request.method !== "POST") {
169 |             event.respondWith(fetch(event.request));
170 |             return;
171 |           }
172 | 
173 |           const formDataPromise = event.request.formData();
174 |           event.respondWith(
175 |             formDataPromise.then((formData) => {
176 |               const link = formData.get("link") || "";
177 |               saveBookmark(link);
178 |               return new Response(`Bookmark saved: ${link}`);
179 |             })
180 |           );
181 |         });
182 |       
183 |

184 | How the handler deals with the shared data is at the handler's 185 | discretion, and will generally depend on the type of app. Here are some 186 | suggestions: 187 |

188 | 203 |
204 |
205 |

206 | Extension to the Web App Manifest 207 |

208 |

209 | As a [=manifest=] is JSON, this specification relies on 211 | the types defined in the [[JSON]] specification: namely object and string. 214 |

215 |

216 | The following steps are added to the [=processing extension-point of 217 | web manifest=]: 218 |

219 |
    220 |
  1. Let |json| and |manifest| be the corresponding variables from 221 | [=processing a manifest=]. 222 |
  2. 223 |
  3. [=Process the `share_target` member=] with |json| and |manifest|. 224 |
  4. 225 |
226 |
227 |

228 | `share_target` member 229 |

230 |

231 | The share_target 232 | member of the manifest is an [=object=]. When present, it declares 233 | this application to be a web share target, and describes how 234 | the application receives share data. 235 |

236 |

237 | A web share target is a web site with a 238 | valid [=manifest=] containing a [=manifest/share_target=] member. 239 |

240 |

241 | A web share target is a type of share target (other types can 242 | be available, e.g., some system applications). 243 |

244 |

245 | To process the `share_target` member given [=object=] 246 | |json:JSON| and [=ordered map=] |manifest:ordered map|: 247 |

248 |
    249 |
  1. If |json|["share_target"] is not an [=object=], return. 250 |
  2. 251 |
  3. Let |target:object| be |json|["share_target"]. 252 |
  4. 253 |
  5. If |target|["action"] or |target|["params"] is missing, return. 254 |
  6. 255 |
  7. Process [=ShareTarget/action=]: 256 |
      257 |
    1. Let |action:URL| be the result of [=URL parser|parsing=] 258 | |share target|["action"] relative to the |manifest URL| and with 259 | no encoding override. If the result is failure, return. 260 |
    2. 261 |
    3. If |action| is not [=URL/within scope=] of the 262 | |manifest|["scope"], return. 263 |
    4. 264 |
    5. If the [=url/origin=] of |action| is not a [=potentially 265 | trustworthy origin=], return. 266 |
    6. 267 |
    268 |
  8. 269 |
  9. Let |method:string| be "GET". 270 |
  10. 271 |
  11. If |target|["method"] is present, process [=ShareTarget/method=]: 272 |
      273 |
    1. If |target|["method"] is neither an [=ASCII 274 | case-insensitive=] match for the strings `"GET"` nor `"POST"`, 275 | return. 276 |
    2. 277 |
    3. Set |method| to [=ASCII uppercase=] |target|["method"]. 278 |
    4. 279 |
    280 |
  12. 281 |
  13. Let |enctype:string| be "application/x-www-form-urlencoded". 282 |
  14. 283 |
  15. If |method| is `"POST"`: 284 |
      285 |
    1. If |target|["enctype"] is neither an [=ASCII 286 | case-insensitive=] match for the strings 287 | `"application/x-www-form-urlencoded"` nor 288 | `"multipart/form-data"`, return. 289 |
    2. 290 |
    3. Set |enctype| to [=ASCII lowercase=] |target|["enctype"]. 291 |
    4. 292 |
    293 |
  16. 294 |
  17. Let |params:ordered map| be a new [=ordered map=]. 295 |
  18. 296 |
  19. Process [=ShareTarget/params=]: 297 |
      298 |
    1. [=List/For each=] |member:string| of « "title", "text", "url" 299 | »: 300 |
        301 |
      1. If |target|["param"] doesn't have a property |member|, 302 | continue. 303 |
      2. 304 |
      3. If |target|["param"][member] is not a [=string=], return. 305 |
      4. 306 |
      5. Set |params|[member] to |target|["param"][member]. 307 |
      6. 308 |
      309 |
    2. 310 |
    311 |
  20. 312 |
  21. Set |manifest|["share_target"] to [=ordered map=] «[
    313 | "action" → [=URL serializer|serialize=] |action|,
    314 | "enctype" → |enctype|,
    315 | "method" → |method|,
    316 | "params" → |params|,
    317 | ]». 318 |
  22. 319 |
320 |
321 |
322 |

323 | `ShareTarget` and its members 324 |

325 |

326 | The ShareTarget [=object=] can have the following members: 327 |

328 |
329 |
330 | action member 331 |
332 |
333 | A [=string=] that specifies the [=URL=] for the [=web share 334 | target=]. 335 |
336 |
337 | method member 338 |
339 |
340 | A [=string=] that specifies the HTTP [=request=] [=request/method=] 341 | for the [=web share target=]. 342 | 348 |
349 |
350 | enctype member 351 |
352 |
353 | A [=string=] that specifies how the share data is encoded in the 354 | body of a `POST` request. It is ignored when [=method=] is `"GET"`. 355 |
356 |
357 | params member 358 |
359 |
360 | A ShareTargetParams [=object=]. 361 |
362 |
363 |
364 |
365 |

366 | `ShareTargetParams` and its members 367 |

368 |

369 | The ShareTargetParams [=object=] can have the following 370 | members: 371 |

372 |
373 |
374 | title 375 | member 376 |
377 |
378 | A [=string=] that specifies the name of the query parameter used 379 | for the title of the document being shared. 380 |
381 |
382 | text 383 | member 384 |
385 |
386 | A [=string=] that specifies the name of the query parameter used 387 | for the arbitrary text that forms the body of the message being 388 | shared. 389 |
390 |
391 | url member 392 |
393 |
394 | A [=string=] that specifies the name of the query parameter used 395 | for the URL string referring to a resource being shared. 396 |
397 |
398 |
399 |
400 |
401 |

402 | Registration of web share targets 403 |

404 |

405 | How and when web share targets are "registered" is at the 406 | discretion of the user agent and/or the end user. In fact, 407 | "registration" is a user-agent-specific concept that is not formally 408 | defined here; user agents are NOT REQUIRED to "register" web share 409 | targets at all; they are only REQUIRED to provide some mechanism to 410 | convey shared data to a web share target of the end user's choosing. 411 | User agents MAY consider a web share target "registered" even if it is 412 | not [=installed web application|installed=] 413 |

414 |

415 | The user agent MAY automatically register all web share targets 416 | as the user visits the site, but it is RECOMMENDED that more discretion 417 | is applied, to avoid overwhelming the user with the choice of a large 418 | number of targets. 419 |

420 |
421 |

422 | Examples of registration strategies that user agents can employ are: 423 |

424 | 438 |
439 |

440 | When presenting the end user with a list of web share targets, 441 | the user agent MAY use an online service which has pre-indexed 442 | manifests, and therefore show the user targets that they have never 443 | visited or explicitly registered. 444 |

445 |
446 |
447 |
448 |

449 | Handling incoming shares 450 |

451 |

452 | A web share target is invoked when the end user is sharing some 454 | data intended for a generic application, and indicates that specific 455 | web share target as the receiver of the data. 456 |

457 |

458 | It is not specified where the data comes from, or how the end user 459 | indicates the web share target as the receiver. However, one possible 460 | source is a call to {{Navigator}}'s {{Navigator/share()}} method in the 461 | same user agent. 462 |

463 |
464 |

465 | Examples of other possible sources of a web share target 466 | invocation are: 467 |

468 | 478 |
479 |
480 |

481 | Obtaining a `ShareData` 482 |

483 |

484 | When a web share target is invoked, the data MAY be in 485 | an unspecified format. The user agent MUST first convert the data 486 | into a {{ShareData}} object, if it is not already, by mapping to the 487 | members of `ShareData` from equivalent concepts in the host system. 488 | If the source was a call to {{Navigator/share()}}, the user agent 489 | SHOULD use the {{ShareData}} argument unmodified (but this is not 490 | always possible, as it might have to round-trip through some other 491 | format in a lossy manner). The user agent MAY employ heuristics to 492 | map the data onto the `ShareData` fields as well as possible. 493 |

494 |

495 | For example, the host share system may not have a dedicated URL 496 | field, but a convention that both plain text and URLs are sometimes 497 | transmitted in a "text" field. This is the case on Android. The user 498 | agent can check whether all or part of the "text" field is a [=valid 499 | URL string=], and if so, move that part of the "text" field to the 500 | {{ShareData}}'s {{ShareData/url}} member. 501 |

502 |
503 |
504 |

505 | Launching the web share target 506 |

507 |

508 | When web share target having [=ordered map=] |manifest| is 509 | invoked with {{ShareData}} |data|, run the following steps: 510 |

511 |
    512 |
  1. Let |url:URL| be the result of [=URL parser|parsing=] 513 | |manifest|["share_target"]["action"]. 514 |
  2. 515 |
  3. Let |entries:list| be a new empty [=list=]. 516 |
  4. 517 |
  5. [=List/For each=] |member:string| of « "title", "text", "url" »: 518 |
      519 |
    1. Let |name:string| be the value of 520 | |manifest|["share_target"]["params"][|member|]. 521 |
    2. 522 |
    3. If |name| is `undefined` or the empty string, continue. 523 |
    4. 524 |
    5. If |data|[|member|] is `undefined`, continue. 525 |
    6. 526 |
    7. Let |value:string| be ToString(|data|[|member|]). 528 |
    8. 529 |
    9. [=List/Append=] [=tuple=] (|name|, |value|) to |entry list|. 530 |
    10. 531 |
    532 |
  6. 533 |
  7. Let |header list| be a newly created [=Headers/header list=]. 534 |
  8. 535 |
  9. Let |method:string| be |manifest|["share_target"]["method"]. 536 |
  10. 537 |
  11. Let |enctype:string| be |manifest|["share_target"]["enctype"]. 538 |
  12. 539 |
  13. If |method| is `"GET"`: 540 |
      541 |
    1. Let |query| be the result of running the [=urlencoded 542 | serializer=] with |entries| and no encoding override. 543 |
    2. 544 |
    3. Set |url|'s [=URL/query=] component to |query|. 545 |
    4. 546 |
    5. Let |body| be null. 547 |
    6. 548 |
    549 |
  14. 550 |
  15. Otherwise, if |method| is `"POST"` and |enctype| is 551 | `"application/x-www-form-urlencoded"`: 552 |
      553 |
    1. Let |body:string| be the result of running the [=urlencoded 554 | serializer=] with |entries| and no encoding override. 555 |
    2. 556 |
    3. Set |body| to the result of [=UTF-8 encode=] |body|. 557 |
    4. 558 |
    5. [=header list/Append=] 559 | `"Content-Type"`/`"application/x-www-form-urlencoded"` to |header 560 | list|. 561 |
    6. 562 |
    563 |
  16. 564 |
  17. Otherwise, if |method| is `"POST"` and | enctype| is 565 | `"multipart/form-data"`: 566 |
      567 |
    1. Let |body| be the result of running the 568 | multipart/form-data encoding algorithm with |entries| and 569 | the [=UTF-8=] encoding. 570 |
    2. 571 |
    3. Let |MIME type:string| be the concatenation of the string 572 | `"multipart/form-data;"`, a U+0020 SPACE character, the string 573 | `"boundary="`, and the [=`multipart\/form-data` boundary string=] 574 | generated by the [=`multipart\/form-data` encoding algorithm=]. 575 |
    4. 576 |
    5. [=header list/Append=] `"Content-Type"`/|MIME type| to 577 | |header list|. 578 |
    6. 579 |
    580 |
  18. 581 |
  19. Let |browsing context| be the result of creating a 582 | new 583 | [=top-level browsing context=]. 584 |
  20. 585 |
  21. Let |request:Request| be a new [=Request=] whose method is 586 | |method|, url is |url|, header list is |header list|, and body is 587 | |body|. 588 |
  22. 589 |
  23. [=Navigate=] |browsing context| to |request =| 590 |
  24. 591 |
592 |

593 | This algorithm assumes that |manifest| has had the [=process the 594 | `share_target` member=] algorithm run on it and still has a 595 | [=manifest/share_target=] afterwards. 596 |

597 |
598 |
599 |
600 |

601 | Accessibility 602 |

603 |

604 | This specification has no known accessibility considerations. 605 |

606 |
607 |
608 |

609 | Security and privacy considerations 610 |

611 | 639 |
640 |
641 |
642 |

643 | Acknowledgments 644 |

645 |

646 | Thanks to the [[[WEBINTENTS]]] team, who laid the groundwork for the 647 | web app interoperability use cases. In particular, Paul Kinlan, who did a lot of early 649 | advocacy for Web Share and Web Share Target. 650 |

651 |

652 | Thanks to Connie Pyromallis, who wrote an early draft of this spec, and 653 | helped design and prototype the API. 654 |

655 |

656 | Thanks to Alex Russell and David Baron, for their feedback on early 657 | drafts of this spec. 658 |

659 |
660 |
661 | 662 | 663 | -------------------------------------------------------------------------------- /level-2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Web Share Target API - Level 2 7 | 8 | 10 | 53 | 54 | 55 |
56 |

57 | This specification defines an API that allows websites to declare 58 | themselves as web share targets, which can receive shared 59 | content from either the Web Share API, or 60 | system events (e.g., shares from native apps). 61 |

62 |

63 | This is a similar mechanism to navigator.registerProtocolHandler, 65 | in that it works by registering the website with the user agent, to 66 | later be invoked from another site or native application via the 67 | user agent (possibly at the discretion of the user). The difference is 68 | that registerProtocolHandler 70 | registers the handler via a programmatic API, whereas a Web Share 71 | Target is declared in the Web App 72 | Manifest, to be registered at a time of the user agent or user's 73 | choosing. 74 |

75 |
76 |
77 |
78 |

79 | Prerequisites 80 |

81 |

82 | In order to implement this API, it is REQUIRED that the user agent 83 | implements Web App Manifest. This spec 84 | also re-uses some definitions from the Web 85 | Share API spec. Implementation of that spec is NOT REQUIRED to 86 | implement this one (but it is RECOMMENDED). 87 |

88 |
89 |
90 |

91 | Usage Example 92 |

93 |

94 | To register a site as a share target, a share_target entry is 95 | added to the Web App Manifest, as shown: 96 |

97 |
 98 | {
 99 |   "name": "Includinator",
100 |   "share_target": {
101 |     "action": "share.html",
102 |     "params": {
103 |       "title": "name",
104 |       "text": "description",
105 |       "url": "link"
106 |     }
107 |   }
108 | }
109 | 
110 |

111 | The params keys correspond to the 112 | key names in ShareData from 113 | [[WebShare]], while the values are arbitrary names that will be used as 114 | query parameters when the target is launched. 115 |

116 |

117 | When a share takes place, if the user selects this share target, the 118 | user agent opens a new browsing context at the action URL, 119 | with query parameter values containing the shared data, just like an 120 | HTML form submission. 121 |

122 |

123 | For the purpose of this example, we assume the manifest is located at 124 | https://example.org/includinator/manifest.webmanifest. 125 |

126 |
127 | <html>
128 |   <head>
129 |     <link rel="manifest" href="manifest.webmanifest">
130 |   </head>
131 |   <body>
132 |     <script>
133 |       window.addEventListener('load', () => {
134 |         var parsedUrl = new URL(window.location);
135 |         console.log('Title shared: ' + parsedUrl.searchParams.get('name'));
136 |         console.log('Text shared: ' + parsedUrl.searchParams.get('description'));
137 |         console.log('URL shared: ' + parsedUrl.searchParams.get('link'));
138 |       });
139 |     </script>
140 |   </body>
141 | </html>
142 | 
143 |

144 | If an incoming share contains the title "My News" and the URL 145 | http://example.com/news, the user agent will open a new 146 | window or tab and navigate to: 147 |

148 |
149 | https://example.org/includinator/share.html?name=My+News&link=http%3A%2F%2Fexample.com%2Fnews
150 | 
151 |

152 | U+0020 (SPACE) characters are encoded as "+", due to the 153 | use of application/x-www-form-urlencoded 155 | encoding, not "%20" as might be expected. Processors must 156 | take care to decode U+002B (+) characters as U+0020 (SPACE), which some 157 | URL decoding libraries, including ECMAScript's decodeURIComponent 159 | function, may not do automatically. 160 |

161 |

162 | The query parameters are populated with information from the 163 | ShareData being 164 | shared. If the ShareData contains no information for a given member, 165 | the query parameter is omitted. 166 |

167 |

168 | A share target might only be interested in a subset of the 169 | ShareData members. This 170 | example also shows a share target that receives data as a 171 | POST request, which should be the case if the request 172 | causes an immediate side effect. 173 |

174 |
175 | {
176 |   "name": "Bookmark",
177 |   "share_target": {
178 |     "action": "/bookmark",
179 |     "method": "POST",
180 |     "enctype": "multipart/form-data",
181 |     "params": {
182 |       "url": "link"
183 |     }
184 |   }
185 | }
186 | 
187 |

188 | The shared information might be read by a service worker, rather 190 | than being sent over the network to the server. 191 |

192 |
193 | self.addEventListener('fetch', event => {
194 |   if (event.request.method !== 'POST') {
195 |     event.respondWith(fetch(event.request));
196 |     return;
197 |   }
198 | 
199 |   event.respondWith((async () => {
200 |     const formData = await event.request.formData();
201 |     const link = formData.get('link') || '';
202 |     const responseUrl = await saveBookmark(link);
203 |     return Response.redirect(responseUrl, 303);
204 |   })());
205 | });
206 | 
207 |

208 | A files entry in params is 209 | used to declare that files are accepted by the share target: 210 |

211 |
212 | {
213 |   "name": "Aggregator",
214 |   "share_target": {
215 |     "action": "/cgi-bin/aggregate",
216 |     "method": "POST",
217 |     "enctype": "multipart/form-data",
218 |     "params": {
219 |       "title": "name",
220 |       "text": "description",
221 |       "url": "link",
222 |       "files": [
223 |         {
224 |           "name": "records",
225 |           "accept": ["text/csv", ".csv"]
226 |         },
227 |         {
228 |           "name": "graphs",
229 |           "accept": "image/svg+xml"
230 |         }
231 |       ]
232 |     }
233 |   }
234 | }
235 | 
236 |

237 | The target will be invoked with a 238 | multipart/form-data POST request, with field names as 239 | specified in params. Each shared file is assigned to the first files 240 | entry that accepts its MIME type. (The "POST" 241 | method and "multipart/form-data" 242 | enctype are necessary when file sharing, as with HTML 243 | forms.) 244 |

245 |

246 | How the handler deals with the shared data is at the handler's 247 | discretion, and will generally depend on the type of app. Here are some 248 | suggestions: 249 |

250 | 270 |

271 | As with HTML forms, replying to a POST request with a 303 See Other redirect is highly 273 | recommended, as it avoids the POST being submitted a second time if the 274 | user requests a page refresh. 275 |

276 |
277 |
278 |

279 | Extension to the Web App Manifest 280 |

281 |

282 | The following IDL extends the WebAppManifest dictionary. 284 |

285 |
286 | dictionary ShareTargetFiles {
287 |   required USVString name;
288 |   required (USVString or sequence<USVString>) accept;
289 | };
290 | 
291 | dictionary ShareTargetParams {
292 |   USVString title;
293 |   USVString text;
294 |   USVString url;
295 |   (ShareTargetFiles or sequence<ShareTargetFiles>) files;
296 | };
297 | 
298 | dictionary ShareTarget {
299 |   required USVString action;
300 |   DOMString method = "GET";
301 |   DOMString enctype = "application/x-www-form-urlencoded";
302 |   required ShareTargetParams params;
303 | };
304 | 
305 | partial dictionary WebAppManifest {
306 |   ShareTarget share_target;
307 | };
308 | 
309 |

310 | The following steps are added to the extension point in the steps for 312 | processing a 313 | manifest: 314 |

315 |
    316 |
  1. Set manifest["share_target"] to the result of 317 | running post-processing the share_target member 318 | given manifest["share_target"], 319 | manifest["scope"], and 321 | manifest URL. 322 |
  2. 323 |
324 |
325 |

326 | share_target member 327 |

328 |

329 | The share_target member of the manifest is a 330 | ShareTarget dictionary that declares this application to be a 331 | web share target, and describes how the application receives 332 | share data. 333 |

334 |

335 | A web share target is a web 336 | site with a valid manifest containing a share_target member. A 337 | web share target is a type of share target. 339 |

340 |

341 | The steps for post-processing the share_target 342 | member is given by the following algorithm. The algorithm takes 343 | a ShareTarget share target, a URL scope URL, and a URL manifest URL. This algorithm 346 | returns a ShareTarget or undefined. 347 |

348 |
    349 |
  1. If share target is undefined, then return 350 | undefined. 351 |
  2. 352 |
  3. If share target["method"] is neither an ASCII case-insensitive match for 355 | the strings "GET" nor "POST", 356 | issue a 357 | developer warning that the method is not supported, and return 358 | undefined. 359 |
  4. 360 |
  5. If share target["method"] is an ASCII case-insensitive match for 363 | the string "GET" and share 364 | target["enctype"] is not an 365 | ASCII 366 | case-insensitive match for the string 367 | "application/x-www-form-urlencoded", issue a developer 369 | warning that the enctype is not supported with method GET, and 370 | return undefined. 371 |
  6. 372 |
  7. If share target["method"] is an ASCII case-insensitive match for 375 | the string "POST" and share 376 | target["enctype"] is neither 377 | an ASCII 378 | case-insensitive match for the strings 379 | "application/x-www-form-urlencoded" nor 380 | "multipart/form-data", issue a developer 382 | warning that the enctype is not supported, and return 383 | undefined. 384 |
  8. 385 |
  9. If share target["params"]["files"] is a ShareTargetFiles 388 | dictionary, bucket, set share 389 | target["params"]["files"] to «bucket». 392 |
  10. 393 |
  11. If share target["params"]["files"] contains one or more 396 | ShareTargetFiles dictionaries, and share 397 | target["method"] is not an 398 | ASCII 399 | case-insensitive match for the string "POST", or 400 | share target["enctype"] 401 | is not an ASCII 402 | case-insensitive match for the string 403 | "multipart/form-data", issue a developer 405 | warning that files are only supported with multipart/form-data 406 | POST, and return undefined. 407 |
  12. 408 |
  13. For each bucket in share 409 | target["params"]["files"]: 412 |
      413 |
    1. If bucket["name"] is an empty string, issue a developer 416 | warning, remove bucket from share 417 | target["params"]["files"] and continue. 421 |
    2. 422 |
    3. If bucket["accept"] is a USVString, accept, set 425 | bucket["accept"] to «accept». 427 |
    4. 428 |
    5. For each string in bucket["accept"] that does not match any of the 430 | following, issue a developer 432 | warning and remove the string from 433 | bucket["accept"]. 435 |
        436 |
      • a string whose first character is a U+002E FULL STOP 437 | character (.) 438 |
      • 439 |
      • 440 | type/subtype (where 441 | type and subtype are RFC7230 tokens) 443 |
      • 444 |
      • 445 | type/* (where type is a 446 | RFC7230 token) 447 |
      • 448 |
      • 449 | */* 450 |
      • 451 |
      452 |
    6. 453 |
    7. If bucket["accept"] is empty, issue a developer 456 | warning and remove bucket from share 457 | target["params"]["files"]. 460 |
    8. 461 |
    462 |
  14. 463 |
  15. Let action be the result of parsing the URL share 466 | target["action"], relative 467 | to the manifest URL and with no encoding override. If 468 | the result is failure, issue a developer 470 | warning and return undefined. 471 |
  16. 472 |
  17. If action is not within scope of scope 474 | URL, 475 | issue a developer warning that action is outside of the navigation scope , and 478 | return undefined. 479 |
  18. 480 |
  19. If the origin of 481 | action is not potentially 483 | trustworthy, issue a developer 485 | warning and return undefined. 486 |
  20. 487 |
  21. Set share target["action"] to action. 489 |
  22. 490 |
  23. Return share target. 491 |
  24. 492 |
493 |
494 |
495 |

496 | ShareTarget and its members 497 |

498 |

499 | The ShareTarget dictionary contains the following members. 500 |

501 |

502 | The action member specifies the URL for the web share target. 505 |

506 |

507 | The method member specifies the HTTP request method for the 509 | web share target. 510 |

511 |

512 | A use case for GET requests is when the share target 513 | drafts a message for subsequent user approval. If the share target 514 | performs a side-effect without any user interaction, 515 | POST requests should be used. 516 |

517 |

518 | The user agent MAY truncate the share data if method is 519 | "GET" and an entry in the share data exceeds 2000 bytes. 520 |

521 |

522 | The enctype member specifies how the share data is encoded 523 | in the request. 524 |

525 |

526 | The params member contains a ShareTargetParams 527 | dictionary. 528 |

529 |
530 |
532 |

533 | ShareTargetParams and its members 534 |

535 |

536 | The ShareTargetParams dictionary contains the following 537 | members. 538 |

539 |

540 | The title member specifies the name of the query parameter 541 | used for the title of the document being shared. 542 |

543 |

544 | The text member specifies the name of the query parameter 545 | used for the arbitrary text that forms the body of the message being 546 | shared. 547 |

548 |

549 | The url member specifies the name of the query parameter 550 | used for the URL string referring to a resource being shared. 551 |

552 |

553 | The files member contains zero or more 554 | ShareTargetFiles dictionaries. 555 |

556 |
557 |
559 |

560 | ShareTargetFiles and its members 561 |

562 |

563 | The ShareTargetFiles dictionary contains the following 564 | members. 565 |

566 |

567 | The name member specifies the name of the form field used 568 | to share the files. 569 |

570 |

571 | The accept member specifies a sequence of accepted MIME 572 | type(s) or file extension(s), the latter expressed as strings 573 | starting with U+002E FULL STOP (.). 574 |

575 |
576 |
577 |
578 |

579 | Registration of web share targets 580 |

581 |

582 | How and when web share targets are "registered" is at the 583 | discretion of the user agent and/or the end user. In fact, 584 | "registration" is a user-agent-specific concept that is not formally 585 | defined here; user agents are NOT REQUIRED to "register" web share 586 | targets at all; they are only REQUIRED to provide some mechanism to 587 | convey shared data to a web share target of the end user's choosing. 588 | User agents MAY consider a web share target "registered" even if it is 589 | not installed. 590 |

591 |

592 | The user agent MAY automatically register all web share targets 593 | as the user visits the site, but it is RECOMMENDED that more discretion 594 | is applied, to avoid overwhelming the user with the choice of a large 595 | number of targets. 596 |

597 |
598 |

599 | Examples of registration strategies that user agents can employ are: 600 |

601 | 615 |
616 |

617 | When presenting the end user with a list of web share targets, 618 | the user agent MAY use an online service which has pre-indexed 619 | manifests, and therefore show the user targets that they have never 620 | visited or explicitly registered. 621 |

622 |
623 |

624 | An implementation supports filtering on MIME types if it 625 | takes into account the MIME types of the files being shared when 626 | presenting the user with a choice of share targets. 627 |

628 |

629 | An implementation supports filtering on file extensions if 630 | it takes into account the extensions in the names of the files being 631 | shared when presenting the user with a choice of share targets. 632 |

633 |

634 | An implementation MUST support filtering on MIME types or 635 | filtering on file extensions, or both. 636 |

637 |

638 | If a file being shared is not accepted by any of a share 639 | target's files entries, the 640 | user MUST NOT be presented with that web share target as an option. 641 |

642 |
643 |
644 |

645 | Handling incoming shares 646 |

647 |

648 | A web share target is invoked when the end user is sharing some 650 | data intended for a generic application, and indicates that specific 651 | web share target as the receiver of the data. 652 |

653 |

654 | It is not specified where the data comes from, or how the end user 655 | indicates the web share target as the receiver. However, one possible 656 | source is a call to navigator.share in the 658 | same user agent. 659 |

660 |
661 |

662 | Examples of other possible sources of a web share target 663 | invocation are: 664 |

665 | 676 |
677 |
678 |

679 | Obtaining a ShareData 680 |

681 |

682 | When a web share target is invoked, the data MAY be in 683 | an unspecified format. The user agent MUST first convert the data 684 | into a ShareData dictionary, if 686 | it is not already, by mapping to the fields of ShareData 687 | from equivalent concepts in the host system. If the source was a call 688 | to navigator.share, the 690 | user agent SHOULD use the ShareData argument 692 | unmodified (but this is not always possible, as it might have to 693 | round-trip through some other format in a lossy manner). The user 694 | agent MAY employ heuristics to map the data onto the 695 | ShareData fields as well as possible. 696 |

697 |

698 | For example, the host share system may not have a dedicated URL 699 | field, but a convention that both plain text and URLs are sometimes 700 | transmitted in a "text" field. This is the case on Android. The user 701 | agent can check whether all or part of the "text" field is a 702 | valid URL string, and if so, 703 | move that part of the "text" field to the ShareData's url field. 706 |

707 |
708 |
709 |

710 | Launching the web share target 711 |

712 |

713 | When web share target having WebAppManifest 714 | manifest is invoked with ShareData data, 716 | run the following steps: 717 |

718 |
    719 |
  1. Let url be a copy of 720 | manifest["share_target"]["action"]. 722 |
  2. 723 |
  3. Let entry list be a new empty list of name-value 724 | tuples. Each value can be either a USVString or a File list. 727 |
  4. 728 |
  5. For each member in the sequence « 729 | "title", "text", "url" », 730 |
      731 |
    1. Let name be the value of 732 | manifest["share_target"]["params"][member]. 734 |
    2. 735 |
    3. If name is undefined or an empty 736 | string, then continue. 737 |
    4. 738 |
    5. Let value be the value of 739 | data[member]. 740 |
    6. 741 |
    7. If value is undefined, then continue. 742 |
    8. 743 |
    9. If method is "GET" and 744 | value exceeds 2000 bytes in length, the user agent MAY 745 | set value to the first 2000 bytes of value. 746 |
    10. 747 |
    11. 748 | Append to entry 749 | list a tuple with name and value. 750 |
    12. 751 |
    752 |
  6. 753 |
  7. Let accepted be an empty map of 754 | name/File list 755 | pairs. 756 |
  8. 757 |
  9. For each bucket in the sequence 758 | manifest["share_target"]["params"]["files"], 761 |
      762 |
    1. Set 763 | accepted[bucket["name"]] 764 | to a new empty File list. 766 |
    2. 767 |
    768 |
  10. 769 |
  11. For each shared file in the sequence 770 | data["files"], 771 |
      772 |
    1. Find the first bucket in the sequence 773 | manifest["share_target"]["params"]["files"] that accepts shared 776 | file. 777 |
    2. 778 |
    3. Append shared file to 779 | accepted[bucket["name"]]. 781 |
    4. 782 |
    783 |
  12. 784 |
  13. For each bucket in the sequence 785 | manifest["share_target"]["params"]["files"], 788 |
      789 |
    1. Let name be the value of 790 | bucket["name"]. 792 |
    2. 793 |
    3. Let value be the value of 794 | accepted[name], a (possibly empty) 795 | File list. 796 |
    4. 797 |
    5. 798 | Append to entry 799 | list a tuple with name and value. 800 |
    6. 801 |
    802 |
  14. 803 |
  15. Let header list be a new empty header list. 805 |
  16. 806 |
  17. Let method be 807 | manifest["share_target"]["method"]. 809 |
  18. 810 |
  19. Let enctype be 811 | manifest["share_target"]["enctype"]. 813 |
  20. 814 |
  21. If method is "GET": 815 |
      816 |
    1. Let query be the result of running the 817 | application/x-www-form-urlencoded 819 | serializer with entry list and no encoding 820 | override. 821 |
    2. 822 |
    3. Set url's query component to query. 824 |
    4. 825 |
    5. Let body be null. 826 |
    6. 827 |
    828 |
  22. 829 |
  23. Otherwise, if method is "POST" and 830 | enctype is "application/x-www-form-urlencoded": 831 |
      832 |
    1. Let body be the result of running the 833 | application/x-www-form-urlencoded 834 | serializer with entry list and no encoding 835 | override. 836 |
    2. 837 |
    3. Set body to the result of encoding body. 839 |
    4. 840 |
    5. 841 | Append 842 | "Content-Type"/"application/x-www-form-urlencoded" 843 | to header list. 844 |
    6. 845 |
    846 |
  24. 847 |
  25. Otherwise, if method is "POST" and 848 | enctype is "multipart/form-data": 849 |
      850 |
    1. Let body be the result of running the 851 | 852 | multipart/form-data encoding algorithm with 853 | entry list and UTF-8 854 | encoding. 855 |
    2. 856 |
    3. Let MIME type be the concatenation of the string 857 | "multipart/form-data;", a U+0020 SPACE character, 858 | the string "boundary=", and the multipart/form-data 860 | boundary string generated by the multipart/form-data 862 | encoding algorithm. 863 |
    4. 864 |
    5. 865 | Append 866 | "Content-Type"/MIME type to header 867 | list. 868 |
    6. 869 |
    870 |
  26. 871 |
  27. Let browsing context be the result of creating a 872 | new 873 | top-level browsing 874 | context. 875 |
  28. 876 |
  29. 877 | Navigate browsing 878 | context to a new request whose method is 880 | method, url is url, header list is 881 | header list, and body is body. 882 |
  30. 883 |
884 |

885 | This algorithm assumes that manifest has had the 886 | post-processing the share_target member algorithm 887 | run on it and still has a share_target afterwards. 888 |

889 |
890 |
891 |

892 | Determining if a file is accepted 893 |

894 |

895 | The algorithm for determining if a ShareTargetFiles 896 | bucket accepts a 897 | File shared file 898 | is as follows:- 899 |

900 |
    901 |
  1. For each criterion in 902 | bucket["accept"]: 903 |
      904 |
    1. If the first character of criterion is a U+002E 905 | FULL STOP character (.) and shared 906 | file["name"] ends with criterion, 907 | and the implementation supports filtering on file 908 | extensions, return true. 909 |
    2. 910 |
    3. If criterion is 911 | type/subtype (where 912 | type and subtype are RFC7230 tokens) and matches the MIME 914 | type of shared file, and the implementation supports 915 | filtering on MIME types, return true. 916 |
    4. 917 |
    5. If criterion is type/* 918 | (where type is a RFC7230 token) and the MIME type of 920 | shared file is a subtype of type, and the 921 | implementation supports filtering on MIME types, return 922 | true. 923 |
    6. 924 |
    7. If criterion is */*, return 925 | true. 926 |
    8. 927 |
    928 |
  2. 929 |
  3. Return false. 930 |
  4. 931 |
932 |
933 |
934 |
935 |

936 | Security and privacy considerations 937 |

938 | 967 |
968 |
969 |

970 | Acknowledgments 971 |

972 |

973 | Thanks to the Web Intents team, who laid 974 | the groundwork for the web app interoperability use cases. In 975 | particular, Paul Kinlan, who did 976 | a lot of early advocacy for Web Share and Web Share Target. 977 |

978 |

979 | Thanks to Connie Pyromallis, who wrote an early draft of this spec, and 980 | helped design and prototype the API. 981 |

982 |

983 | Thanks to Alex Russell and David Baron, for their feedback on early 984 | drafts of this spec. 985 |

986 |
987 | 988 | 989 | -------------------------------------------------------------------------------- /tidyconfig.txt: -------------------------------------------------------------------------------- 1 | char-encoding: utf8 2 | indent: yes 3 | indent-spaces: 2 4 | wrap: 80 5 | tidy-mark: no 6 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": ["80485"] 3 | , "contacts": ["marcoscaceres"] 4 | , "shortName": "web-share-target", 5 | "repo-type": "cg-report" 6 | } 7 | --------------------------------------------------------------------------------