Skip to content

Commit 627de10

Browse files
committed
docs: URL protocol handler example
1 parent b0784de commit 627de10

File tree

13 files changed

+272
-16
lines changed

13 files changed

+272
-16
lines changed

.vscode/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"files.associations": {
3+
"*.pmd": "markdown"
4+
}
5+
}

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,26 @@ They live under the [`/src/transformers`][transformers] folder:
9292

9393
<Tabs>
9494
<TabItem value="electron" label="Electron" default>
95+
9596
{@import ./electron.pmd}
97+
9698
</TabItem>
9799
<TabItem value="pwa" label="PWA">
100+
98101
{@import ./pwa.pmd}
102+
99103
</TabItem>
100104
<TabItem value="wv2" label="WebView2">
105+
101106
{@import ./wv2.pmd}
107+
102108
</TabItem>
103109
</Tabs>;
104110
```
105111
112+
**CAUTION:** It is important to leave a blank link before and after `{@import}, otherwise it
113+
will not work.
114+
106115
`partial-content` files can also make use of `import-code`.
107116

108117

data/examples/index.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,8 @@
423423
"name": "URL protocol handler",
424424
"electron": {
425425
"gist": "",
426-
"status": "",
427-
"documentation": ""
426+
"status": "",
427+
"documentation": "https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app"
428428
},
429429
"pwa": {
430430
"gist": "",

docs/examples-list.pmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
| Run on OS login | | | | |
1818
| Serial access |✅ | | | |
1919
| URL handler | | | | |
20-
| URL protocol handler | | | | |
20+
| [URL protocol handler](/docs/examples/url-protocol-handler) |✅ ([Docs](https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app)) | | | |
2121
| USB access | | | | |
2222
| Using local fonts | | | | |
2323
| Video codecs | | | | |

docs/examples.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ These are all the features we have planned to have examples for. Those that are
44
are linked from the feature name. There are also links to more in depth documentation for each
55
technology when found.
66

7-
{@import ./examples-list.pmd}
7+
{@import ./examples-list.pmd}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import Tabs from '@theme/Tabs';
2+
import TabItem from '@theme/TabItem';
3+
import LaunchButton from '../../src/components/LaunchButton';
4+
5+
# URL protocol handler
6+
7+
It's fairly common to find web pages link to resources using non-http protocols. An example is the `mailto:` protocol:
8+
9+
```html
10+
<a href="mailto:[email protected]">Web Master</a>
11+
```
12+
13+
You might want your application to become the handler for a particular protocol well-known protocol (if you are an email
14+
application, for example) or even create your own custom protocol to handle links that navigate to particular
15+
places or states in your application. This is particularly useful when sharing some content with contacts and a common
16+
scenario many applications need to implement.
17+
18+
## Implementation
19+
20+
<Tabs>
21+
<TabItem value="electron" label="Electron" default>
22+
23+
{@import ./url-protocol-handler/electron.pmd}
24+
25+
If you have Fiddle installed, you can run a full example by clicking the following button:
26+
<LaunchButton url="https://fiddle.electronjs.org/launch?target=electron/v14.0.1/docs/fiddles/system/protocol-handler/launch-app-from-URL-in-another-app"></LaunchButton>
27+
28+
</TabItem>
29+
<TabItem value="pwa" label="PWA">
30+
31+
{@import ./url-protocol-handler/pwa.pmd}
32+
33+
</TabItem>
34+
<TabItem value="wv2" label="WebView2">
35+
36+
{@import ./url-protocol-handler/webview2.pmd}
37+
38+
</TabItem>
39+
</Tabs>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
To register a custom protocol in your Electron application you can use the
2+
[`setAsDefaultProtocolClient`][protocol] method of the [`app`][app] module.
3+
4+
```js {4}
5+
const { app } = require('electron');
6+
7+
app.whenReady().then(() => {
8+
app.setAsDefaultProtocolClient('mycustomprotocol');
9+
});
10+
```
11+
12+
Once registered, your application will be opened anytime a user interacts with a link
13+
similar to `mycustomprotocol://custom/path`. How the application access the URI is
14+
different depending on the platform.
15+
16+
For macOS is via the [`open-url`][open-url] event
17+
18+
```js
19+
const { app } = require('electron');
20+
21+
app.on('open-url', (event, url) => {
22+
// Your code handling here
23+
});
24+
```
25+
26+
On Windows the URI is passed as part of the arguments (`process.argv`) when starting the
27+
application. Because the position could change, it is recommended to iterate to find the
28+
right one. An optimistic implementation would be:
29+
30+
```js
31+
const protocolArg = process.argv.find((arg) => arg.startsWith('mycustomprotocol://'));
32+
if(!protocolArg){
33+
return;
34+
}
35+
36+
// Your code handling here
37+
```
38+
39+
:::caution
40+
Depending on the platform there might be some extra steps to do (like add the protocol to
41+
the app's `info.plist` on macOS). Make sure to read the [official documentation][protocol].
42+
:::
43+
44+
Contrary to the PWA case, there are no limitations on the name of the protocol, thus
45+
why the name is `mycustomprotocol` and not `web+mycustomprotocol`.
46+
47+
[app]: https://www.electronjs.org/docs/latest/api/app/
48+
[open-url]: https://www.electronjs.org/docs/latest/api/app/#event-open-url-macos
49+
[protocol]: https://www.electronjs.org/docs/latest/api/app/#appsetasdefaultprotocolclientprotocol-path-args
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
When building a PWA there are 2 ways to register the applications as a protocol handler:
2+
3+
The first one is specifying the `protocol_handlers` property in the [Web app manifest] of the PWA:
4+
5+
```json {12-17}
6+
{
7+
"name": "My PWA",
8+
"orientation": "any",
9+
"icons": [
10+
{
11+
"src": "icon.png",
12+
"sizes": "512x512",
13+
"type": "image/png"
14+
}
15+
],
16+
//...
17+
"protocol_handlers": [
18+
{
19+
"protocol": "web+mycustomprotocol",
20+
"url": "index.html?custom=%s"
21+
}
22+
]
23+
}
24+
```
25+
26+
Or you can do it programatically via JavaScript calling
27+
`navigator.registerProtocolHandler`:
28+
29+
```js {2}
30+
document.addEventListener('DOMContentLoaded', (event) => {
31+
navigator.registerProtocolHandler('web+mycustomprotocol', 'index.html?custom=%s');
32+
})
33+
```
34+
35+
The value of the protocol can be one of the following 2 options:
36+
37+
1. One of the [safelisted schemes]
38+
1. Or begin with `web+` followed by at least one or more lowercase ASCII letters
39+
40+
You can learn more in [this article in web.dev][web.dev].
41+
42+
:::caution
43+
This feature is currently being tested in Chromium browsers. Websites can opt-in via
44+
[origin trials]
45+
:::
46+
47+
[origin trials]: https://developer.chrome.com/origintrials/#/view_trial/1136033006004207617
48+
[safelisted schemes]: https://html.spec.whatwg.org/multipage/system-state.html#safelisted-scheme
49+
[web.dev]: https://web.dev/url-protocol-handler/
50+
[Web app manifest]: https://web.dev/add-manifest/
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
As mentioned in [its page][wv2], WebView2 is available on Windows platforms and can be used from C#
2+
on a WPF or WinForms application, or in a Win32 with C++.
3+
4+
:::caution
5+
While the macOS version of WebView2 is in development, there is no preview available. That said,
6+
registering the application to handle a custom protocol will require calling to OS APIs the same
7+
way it is done on Windows.
8+
:::
9+
10+
To handle the
11+
On a WPF application the URL is passed as part of the parameters when invoking it so you have to
12+
look for it:
13+
14+
```csharp
15+
class Program
16+
{
17+
[STAThread]
18+
static void Main(string[] args)
19+
{
20+
if (args != null && args.Length > 0)
21+
{
22+
if (args[0].StartsWith("mycustomprotocol://"))
23+
{
24+
//handle URI activation
25+
}
26+
27+
App application = new App();
28+
application.InitializeComponent();
29+
application.Run();
30+
}
31+
}
32+
```
33+
34+
If you are using it on a Win32 application with C++, then your could will look similar to:
35+
36+
```cpp
37+
// Parameters are assumed to be valid
38+
39+
HRESULT GetPropVariantForUrlAndTime
40+
(PCWSTR pszUrl, const FILETIME &ftLastModified, PROPVARIANT **ppPropValue)
41+
{
42+
*ppPropValue = NULL;
43+
44+
// Allocate the propvariant pointer.
45+
size_t const cbAlloc = sizeof(**ppPropValue);
46+
*ppPropValue = (PROPVARIANT *)CoTaskMemAlloc(cbAlloc));
47+
48+
HRESULT hr = *ppPropValue ? S_OK : E_OUTOFMEMORY;
49+
50+
if (SUCCEEDED(hr))
51+
{
52+
PropVariantInit(*ppPropValue); // Zero init the value
53+
54+
// Now allocate enough memory for 2 nested PropVariants.
55+
// PKEY_Search_UrlToIndexWithModificationTime is an array of two PROPVARIANTs.
56+
PROPVARIANT *pVector = (PROPVARIANT *)CoTaskMemAlloc(sizeof(*pVector) * 2);
57+
hr = pVector ? S_OK : E_OUTOFMEMORY;
58+
59+
if (SUCCEEDED(hr))
60+
{
61+
// Set the container PROPVARIANT to be a vector of two PROPVARIANTS.
62+
(*ppPropValue)->vt = VT_VARIANT | VT_VECTOR;
63+
(*ppPropValue)->capropvar.cElems = 2;
64+
(*ppPropValue)->capropvar.pElems = pVector;
65+
PWSTR pszUrlAlloc;
66+
hr = SHStrDup(pszUrl, &pszUrlAlloc);
67+
68+
if (SUCCEEDED(hr))
69+
{
70+
// Now fill the array of PROPVARIANTS.
71+
// Put the pointer to the URL into the vector.
72+
(*ppPropValue)->capropvar.pElems[0].vt = VT_LPWSTR;
73+
(*ppPropValue)->capropvar.pElems[0].pwszVal = pszUrlAlloc;
74+
75+
// Put the FILETIME into vector.
76+
(*ppPropValue)->capropvar.pElems[1].vt = VT_FILETIME;
77+
(*ppPropValue)->capropvar.pElems[1].filetime = ftLastModified;
78+
}
79+
80+
else
81+
{
82+
CoTaskMemFree(pVector);
83+
}
84+
}
85+
86+
if (FAILED(hr))
87+
{
88+
CoTaskMemFree(*ppPropValue);
89+
*ppPropValue = NULL;
90+
}
91+
}
92+
return S_OK;
93+
}
94+
```
95+
96+
:::info
97+
Depending on how your application is packaged and distributed (i.e.: MSIX, squirrel, etc.) you might
98+
have to do some extra steps like modifying the registry or a manifest.
99+
:::
100+
You can read more in [Installing and Registering Protocol Handlers].
101+
102+
[Installing and Registering Protocol Handlers]: https://docusaurus.io/docs/markdown-features/admonitions
103+
[wv2]: /docs/webview2

docs/snippets.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

docusaurus.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ module.exports = {
3434
position: 'left',
3535
label: 'Rendering strategies',
3636
},
37+
{
38+
type:'doc',
39+
docId: 'examples',
40+
position: 'left',
41+
label: 'Examples'
42+
},
3743
// {to: '/blog', label: 'Blog', position: 'left'},
3844
{
3945
href: 'https://github.com/crossplatform-dev/crossplatform.dev',

scripts/process-examples-sources.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,12 @@ const normalizeStatus = (status) => {
181181
// TODO: Check if it has been merged
182182
return '🛠';
183183
}
184-
if(status.includes('crbug')){
184+
if (status.includes('crbug')) {
185185
// TODO: Get the resolution of the issue via Playwright because x-xsrf-token and other stuff :(
186186
return 'TBD';
187187
}
188188

189-
return '❓';
189+
return status;
190190
};
191191

192192
/**
@@ -201,14 +201,13 @@ const technologyStateMarkdown = (technology) => {
201201
}
202202

203203
if (technology.status) {
204+
let status = normalizeStatus(technology.status);
205+
204206
if (technology.documentation) {
205207
// TODO: Resolve status links from crbug
206-
return `[${normalizeStatus(technology.status)}](${
207-
technology.documentation
208-
})`;
209-
} else {
210-
return normalizeStatus(technology.status);
208+
status += ` ([Docs](${technology.documentation}))`;
211209
}
210+
return status;
212211
} else {
213212
return '';
214213
}

sidebars.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ module.exports = {
88
items: ['browser-engine', 'platform-controls', 'direct-drawing'],
99
},
1010
],
11+
examples: ['examples', 'examples/url-protocol-handler'],
1112
};

0 commit comments

Comments
 (0)