Skip to content

Commit b716442

Browse files
authored
docs: modernize protocol-handler docs (electron#29380)
* docs: modernize protocol-handler docs * docs: iadd contextIsolation * docs: add guide for launch-app-from-URL-in-other-app * docs: address comments * chore: fix brackets * chore: add escaped brackets
1 parent 0824fc5 commit b716442

File tree

5 files changed

+307
-144
lines changed

5 files changed

+307
-144
lines changed
Lines changed: 71 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,81 @@
11
<!DOCTYPE html>
22
<html>
3-
<head>
4-
<meta charset="UTF-8">
5-
<title>Hello World!</title>
6-
</head>
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
7+
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
8+
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
9+
<title>app.setAsDefaultProtocol Demo</title>
10+
</head>
11+
712
<body>
8-
<section>
9-
<header>
10-
<h1>
11-
Protocol Handler
12-
</h1>
13-
<h3>The <code>app</code> module provides methods for handling protocols.</h3>
14-
<p>These methods allow you to set and unset the protocols your app should be the default app for. Similar to when a browser asks to be your default for viewing web pages.</p>
13+
<h1>App Default Protocol Demo</h1>
14+
15+
<p>The protocol API allows us to register a custom protocol and intercept existing protocol requests.</p>
16+
<p>These methods allow you to set and unset the protocols your app should be the default app for. Similar to when a
17+
browser asks to be your default for viewing web pages.</p>
18+
19+
<p>Open the <a href="https://www.electronjs.org/docs/api/protocol">full protocol API documentation</a> in your
20+
browser.</p>
21+
22+
-----
23+
24+
<h3>Demo</h3>
25+
<p>
26+
First: Launch current page in browser
27+
<button id="open-in-browser" class="js-container-target demo-toggle-button">
28+
Click to Launch Browser
29+
</button>
30+
</p>
31+
32+
<p>
33+
Then: Launch the app from a web link!
34+
<a href="electron-fiddle://open">Click here to launch the app</a>
35+
</p>
36+
37+
----
1538

16-
<p>Open the <a href="https://electronjs.org/docs/api/app">full app API documentation<span class="u-visible-to-screen-reader">(opens in new window)</span></a> in your browser.</p>
17-
</header>
39+
<p>You can set your app as the default app to open for a specific protocol. For instance, in this demo we set this app
40+
as the default for <code>electron-fiddle://</code>. The demo button above will launch a page in your default
41+
browser with a link. Click that link and it will re-launch this app.</p>
1842

19-
<div >
20-
<button id="open-in-browser" class="js-container-target demo-toggle-button">Launch current page in browser
21-
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS <span class="demo-meta-divider">|</span> Process: Main</div>
22-
</button>
23-
<section id='open-app-link'>
24-
<a href="electron-api-demos://open">Now... launch the app from a web link</a>
25-
</section>
26-
<div >
27-
<p>You can set your app as the default app to open for a specific protocol. For instance, in this demo we set this app as the default for <code>electron-api-demos://</code>. The demo button above will launch a page in your default browser with a link. Click that link and it will re-launch this app.</p>
28-
<h5>Packaging</h5>
29-
<p>This feature will only work on macOS when your app is packaged. It will not work when you're launching it in development from the command-line. When you package your app you'll need to make sure the macOS <code>plist</code> for the app is updated to include the new protocol handler. If you're using <code>electron-packager</code> then you can add the flag <code>--extend-info</code> with a path to the <code>plist</code> you've created. The one for this app is below.</p>
30-
<h5>Renderer Process</h5>
31-
<pre><code>
32-
const {shell} = require('electron')
33-
const path = require('path')
34-
const protocolHandlerBtn = document.getElementById('protocol-handler')
35-
protocolHandlerBtn.addEventListener('click', () => {
36-
const pageDirectory = __dirname.replace('app.asar', 'app.asar.unpacked')
37-
const pagePath = path.join('file://', pageDirectory, '../../sections/system/protocol-link.html')
38-
shell.openExternal(pagePath)
39-
})
40-
</code></pre>
41-
<h5>Main Process</h5>
42-
<pre><code>
43-
const {app, dialog} = require('electron')
44-
const path = require('path')
4543

46-
if (process.defaultApp) {
47-
if (process.argv.length >= 2) {
48-
app.setAsDefaultProtocolClient('electron-api-demos', process.execPath, [path.resolve(process.argv[1])])
49-
}
50-
} else {
51-
app.setAsDefaultProtocolClient('electron-api-demos')
52-
}
44+
<h3>Packaging</h3>
45+
<p>This feature will only work on macOS when your app is packaged. It will not work when you're launching it in
46+
development from the command-line. When you package your app you'll need to make sure the macOS <code>plist</code>
47+
for the app is updated to include the new protocol handler. If you're using <code>electron-packager</code> then you
48+
can add the flag <code>--extend-info</code> with a path to the <code>plist</code> you've created. The one for this
49+
app is below:</p>
5350

54-
app.on('open-url', (event, url) => {
55-
dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`)
56-
})
51+
<p>
52+
<h5>macOS plist</h5>
53+
<pre><code>
54+
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
55+
&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
56+
&lt;plist version="1.0"&gt;
57+
&lt;dict&gt;
58+
&lt;key&gt;CFBundleURLTypes&lt;/key&gt;
59+
&lt;array&gt;
60+
&lt;dict&gt;
61+
&lt;key&gt;CFBundleURLSchemes&lt;/key&gt;
62+
&lt;array&gt;
63+
&lt;string&gt;electron-api-demos&lt;/string&gt;
64+
&lt;/array&gt;
65+
&lt;key&gt;CFBundleURLName&lt;/key&gt;
66+
&lt;string&gt;Electron API Demos Protocol&lt;/string&gt;
67+
&lt;/dict&gt;
68+
&lt;/array&gt;
69+
&lt;key&gt;ElectronTeamID&lt;/key&gt;
70+
&lt;string&gt;VEKTX9H2N7&lt;/string&gt;
71+
&lt;/dict&gt;
72+
&lt;/plist&gt;
73+
</code>
74+
</pre>
75+
<p>
5776

58-
</code></pre>
59-
<h5>macOS plist</h5>
60-
<pre><code>
61-
<?xml version="1.0" encoding="UTF-8"?>
62-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
63-
<plist version="1.0">
64-
<dict>
65-
<key>CFBundleURLTypes</key>
66-
<array>
67-
<dict>
68-
<key>CFBundleURLSchemes</key>
69-
<array>
70-
<string>electron-api-demos</string>
71-
</array>
72-
<key>CFBundleURLName</key>
73-
<string>Electron API Demos Protocol</string>
74-
</dict>
75-
</array>
76-
<key>ElectronTeamID</key>
77-
<string>VEKTX9H2N7</string>
78-
</dict>
79-
</plist>
80-
</code>
81-
</pre>
82-
</div>
83-
</div>
84-
<script type="text/javascript">
85-
require('./renderer.js')
86-
</script>
87-
</section>
77+
<!-- You can also require other files to run in this process -->
78+
<script src="./renderer.js"></script>
8879
</body>
89-
</html>
9080

91-
</body>
92-
</html>
81+
</html>
Lines changed: 43 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,63 @@
11
// Modules to control application life and create native browser window
2-
const { app, BrowserWindow, dialog } = require('electron')
2+
const { app, BrowserWindow, ipcMain, shell } = require('electron')
33
const path = require('path')
44

5-
// Keep a global reference of the window object, if you don't, the window will
6-
// be closed automatically when the JavaScript object is garbage collected.
7-
let mainWindow
5+
let mainWindow;
6+
7+
if (process.defaultApp) {
8+
if (process.argv.length >= 2) {
9+
app.setAsDefaultProtocolClient('electron-fiddle', process.execPath, [path.resolve(process.argv[1])])
10+
}
11+
} else {
12+
app.setAsDefaultProtocolClient('electron-fiddle')
13+
}
14+
15+
const gotTheLock = app.requestSingleInstanceLock()
16+
17+
if (!gotTheLock) {
18+
app.quit()
19+
} else {
20+
app.on('second-instance', (event, commandLine, workingDirectory) => {
21+
// Someone tried to run a second instance, we should focus our window.
22+
if (mainWindow) {
23+
if (mainWindow.isMinimized()) mainWindow.restore()
24+
mainWindow.focus()
25+
}
26+
})
27+
28+
// Create mainWindow, load the rest of the app, etc...
29+
app.whenReady().then(() => {
30+
createWindow()
31+
})
32+
33+
app.on('open-url', (event, url) => {
34+
dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`)
35+
})
36+
}
837

938
function createWindow () {
1039
// Create the browser window.
1140
mainWindow = new BrowserWindow({
1241
width: 800,
1342
height: 600,
1443
webPreferences: {
15-
nodeIntegration: true
44+
preload: path.join(__dirname, 'preload.js'),
1645
}
1746
})
1847

19-
// and load the index.html of the app.
2048
mainWindow.loadFile('index.html')
21-
22-
// Open the DevTools.
23-
mainWindow.webContents.openDevTools()
24-
25-
// Emitted when the window is closed.
26-
mainWindow.on('closed', function () {
27-
// Dereference the window object, usually you would store windows
28-
// in an array if your app supports multi windows, this is the time
29-
// when you should delete the corresponding element.
30-
mainWindow = null
31-
})
3249
}
3350

34-
// This method will be called when Electron has finished
35-
// initialization and is ready to create browser windows.
36-
// Some APIs can only be used after this event occurs.
37-
app.whenReady().then(createWindow)
38-
39-
// Quit when all windows are closed.
51+
// Quit when all windows are closed, except on macOS. There, it's common
52+
// for applications and their menu bar to stay active until the user quits
53+
// explicitly with Cmd + Q.
4054
app.on('window-all-closed', function () {
41-
// On macOS it is common for applications and their menu bar
42-
// to stay active until the user quits explicitly with Cmd + Q
43-
if (process.platform !== 'darwin') {
44-
app.quit()
45-
}
46-
})
47-
48-
app.on('activate', function () {
49-
// On macOS it is common to re-create a window in the app when the
50-
// dock icon is clicked and there are no other windows open.
51-
if (mainWindow === null) {
52-
createWindow()
53-
}
55+
if (process.platform !== 'darwin') app.quit()
5456
})
5557

56-
// In this file you can include the rest of your app's specific main process
57-
// code. You can also put them in separate files and require them here.
58-
59-
if (process.defaultApp) {
60-
if (process.argv.length >= 2) {
61-
app.setAsDefaultProtocolClient('electron-api-demos', process.execPath, [path.resolve(process.argv[1])])
62-
}
63-
} else {
64-
app.setAsDefaultProtocolClient('electron-api-demos')
65-
}
66-
67-
app.on('open-url', (event, url) => {
68-
dialog.showErrorBox('Welcome Back', `You arrived from: ${url}`)
58+
// Handle window controls via IPC
59+
ipcMain.on('shell:open', () => {
60+
const pageDirectory = __dirname.replace('app.asar', 'app.asar.unpacked')
61+
const pagePath = path.join('file://', pageDirectory, 'index.html')
62+
shell.openExternal(pagePath)
6963
})
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// All of the Node.js APIs are available in the preload process.
2+
// It has the same sandbox as a Chrome extension.
3+
const { contextBridge, ipcRenderer } = require('electron')
4+
5+
// Set up context bridge between the renderer process and the main process
6+
contextBridge.exposeInMainWorld(
7+
'shell',
8+
{
9+
open: () => ipcRenderer.send('shell:open'),
10+
}
11+
)
Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1-
const { shell } = require('electron')
2-
const path = require('path')
1+
// This file is required by the index.html file and will
2+
// be executed in the renderer process for that window.
3+
// All APIs exposed by the context bridge are available here.
34

4-
const openInBrowserButton = document.getElementById('open-in-browser')
5-
const openAppLink = document.getElementById('open-app-link')
6-
// Hides openAppLink when loaded inside Electron
7-
openAppLink.style.display = 'none'
8-
9-
openInBrowserButton.addEventListener('click', () => {
10-
console.log('clicked')
11-
const pageDirectory = __dirname.replace('app.asar', 'app.asar.unpacked')
12-
const pagePath = path.join('file://', pageDirectory, 'index.html')
13-
shell.openExternal(pagePath)
14-
})
5+
// Binds the buttons to the context bridge API.
6+
document.getElementById('open-in-browser').addEventListener('click', () => {
7+
shell.open();
8+
});

0 commit comments

Comments
 (0)