From c4c78c9b3e2e6cf3bf78d57886a174bf6f9499b2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 May 2017 12:42:53 +0100 Subject: [PATCH] split webContents context handling into own file, as main was growing add handlers for editable fields, with Cut Copy Paste Undo Redo etc add Copy Image feature to all images, working on not only the download buttons now Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- electron_app/src/electron-main.js | 60 +---------- electron_app/src/webcontents-handler.js | 126 ++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 58 deletions(-) create mode 100644 electron_app/src/webcontents-handler.js diff --git a/electron_app/src/electron-main.js b/electron_app/src/electron-main.js index ab844bd37..26f8f972f 100644 --- a/electron_app/src/electron-main.js +++ b/electron_app/src/electron-main.js @@ -24,11 +24,11 @@ const check_squirrel_hooks = require('./squirrelhooks'); if (check_squirrel_hooks()) return; const electron = require('electron'); -const url = require('url'); const tray = require('./tray'); const VectorMenu = require('./vectormenu'); +const webContentsHandler = require('./webcontents-handler'); const windowStateKeeper = require('electron-window-state'); @@ -42,60 +42,12 @@ try { // Continue with the defaults (ie. an empty config) } -const PERMITTED_URL_SCHEMES = [ - 'http:', - 'https:', - 'mailto:', -]; - const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000; const INITIAL_UPDATE_DELAY_MS = 30 * 1000; let mainWindow = null; let appQuitting = false; -function safeOpenURL(target) { - // openExternal passes the target to open/start/xdg-open, - // so put fairly stringent limits on what can be opened - // (for instance, open /bin/sh does indeed open a terminal - // with a shell, albeit with no arguments) - const parsed_url = url.parse(target); - if (PERMITTED_URL_SCHEMES.indexOf(parsed_url.protocol) > -1) { - // explicitly use the URL re-assembled by the url library, - // so we know the url parser has understood all the parts - // of the input string - const new_target = url.format(parsed_url); - electron.shell.openExternal(new_target); - } -} - -function onWindowOrNavigate(ev, target) { - // always prevent the default: if something goes wrong, - // we don't want to end up opening it in the electron - // app, as we could end up opening any sort of random - // url in a window that has node scripting access. - ev.preventDefault(); - safeOpenURL(target); -} - -function onLinkContextMenu(ev, params) { - const popup_menu = new electron.Menu(); - popup_menu.append(new electron.MenuItem({ - label: params.linkURL, - click() { - safeOpenURL(params.linkURL); - }, - })); - popup_menu.append(new electron.MenuItem({ - label: 'Copy Link Address', - click() { - electron.clipboard.writeText(params.linkURL); - }, - })); - popup_menu.popup(); - ev.preventDefault(); -} - function installUpdate() { // for some reason, quitAndInstall does not fire the // before-quit event, so we need to set the flag here. @@ -259,15 +211,7 @@ electron.app.on('ready', () => { } }); - mainWindow.webContents.on('new-window', onWindowOrNavigate); - mainWindow.webContents.on('will-navigate', onWindowOrNavigate); - - mainWindow.webContents.on('context-menu', function(ev, params) { - if (params.linkURL) { - onLinkContextMenu(ev, params); - } - }); - + webContentsHandler(mainWindow.webContents); mainWindowState.manage(mainWindow); }); diff --git a/electron_app/src/webcontents-handler.js b/electron_app/src/webcontents-handler.js new file mode 100644 index 000000000..35785e8b3 --- /dev/null +++ b/electron_app/src/webcontents-handler.js @@ -0,0 +1,126 @@ +const {clipboard, nativeImage, Menu, MenuItem, shell} = require('electron'); +const url = require('url'); + +let webContents; + +const PERMITTED_URL_SCHEMES = [ + 'http:', + 'https:', + 'mailto:', +]; + +function safeOpenURL(target) { + // openExternal passes the target to open/start/xdg-open, + // so put fairly stringent limits on what can be opened + // (for instance, open /bin/sh does indeed open a terminal + // with a shell, albeit with no arguments) + const parsedUrl = url.parse(target); + if (PERMITTED_URL_SCHEMES.indexOf(parsedUrl.protocol) > -1) { + // explicitly use the URL re-assembled by the url library, + // so we know the url parser has understood all the parts + // of the input string + const newTarget = url.format(parsedUrl); + shell.openExternal(newTarget); + } +} + +function onWindowOrNavigate(ev, target) { + // always prevent the default: if something goes wrong, + // we don't want to end up opening it in the electron + // app, as we could end up opening any sort of random + // url in a window that has node scripting access. + ev.preventDefault(); + safeOpenURL(target); +} + +function onLinkContextMenu(ev, params) { + const url = params.linkURL || params.srcURL; + + const popupMenu = new Menu(); + popupMenu.append(new MenuItem({ + label: url, + click() { + safeOpenURL(url); + }, + })); + + if (params.mediaType && params.mediaType === 'image' && !url.startsWith('file://')) { + popupMenu.append(new MenuItem({ + label: 'Copy Image', + click() { + if (url.startsWith('data:')) { + clipboard.writeImage(nativeImage.createFromDataURL(url)); + } else { + ev.sender.copyImageAt(params.x, params.y); + } + }, + })); + } + + popupMenu.append(new MenuItem({ + label: 'Copy Link Address', + click() { + clipboard.writeText(url); + }, + })); + popupMenu.popup(); + ev.preventDefault(); +} + +function _CutCopyPasteSelectContextMenus(params) { + return [{ + role: 'cut', + enabled: params.editFlags.canCut, + }, { + role: 'copy', + enabled: params.editFlags.canCopy, + }, { + role: 'paste', + enabled: params.editFlags.canPaste, + }, { + role: 'pasteandmatchstyle', + enabled: params.editFlags.canPaste, + }, { + role: 'selectall', + enabled: params.editFlags.canSelectAll, + }]; +} + +function onSelectedContextMenu(ev, params) { + const items = _CutCopyPasteSelectContextMenus(params); + const popupMenu = Menu.buildFromTemplate(items); + + popupMenu.popup(); + ev.preventDefault(); +} + +function onEditableContextMenu(ev, params) { + const items = [ + { role: 'undo' }, + { role: 'redo', enabled: params.editFlags.canRedo }, + { type: 'separator' }, + ].concat(_CutCopyPasteSelectContextMenus(params)); + + const popupMenu = Menu.buildFromTemplate(items); + + popupMenu.popup(); + ev.preventDefault(); +} + + +module.exports = (_webContents) => { + webContents = _webContents; + + webContents.on('new-window', onWindowOrNavigate); + webContents.on('will-navigate', onWindowOrNavigate); + + webContents.on('context-menu', function(ev, params) { + if (params.linkURL || params.srcURL) { + onLinkContextMenu(ev, params); + } else if (params.selectionText) { + onSelectedContextMenu(ev, params); + } else if (params.isEditable) { + onEditableContextMenu(ev, params); + } + }); +};