Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: implement clipboard APIs without the remote module on Linux #17200

Merged
merged 1 commit into from
Mar 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions atom/common/api/atom_api_native_image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -659,19 +659,27 @@ bool Converter<mate::Handle<atom::api::NativeImage>>::FromV8(

namespace {

using atom::api::NativeImage;
miniak marked this conversation as resolved.
Show resolved Hide resolved

void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("createEmpty", &atom::api::NativeImage::CreateEmpty);
dict.SetMethod("createFromPath", &atom::api::NativeImage::CreateFromPath);
dict.SetMethod("createFromBitmap", &atom::api::NativeImage::CreateFromBitmap);
dict.SetMethod("createFromBuffer", &atom::api::NativeImage::CreateFromBuffer);
dict.SetMethod("createFromDataURL",
&atom::api::NativeImage::CreateFromDataURL);
dict.SetMethod("createFromNamedImage",
&atom::api::NativeImage::CreateFromNamedImage);
v8::Isolate* isolate = context->GetIsolate();
mate::Dictionary dict(isolate, exports);
dict.Set("NativeImage", NativeImage::GetConstructor(isolate)
->GetFunction(context)
.ToLocalChecked());
mate::Dictionary native_image = mate::Dictionary::CreateEmpty(isolate);
dict.Set("nativeImage", native_image);

native_image.SetMethod("createEmpty", &NativeImage::CreateEmpty);
native_image.SetMethod("createFromPath", &NativeImage::CreateFromPath);
native_image.SetMethod("createFromBitmap", &NativeImage::CreateFromBitmap);
native_image.SetMethod("createFromBuffer", &NativeImage::CreateFromBuffer);
native_image.SetMethod("createFromDataURL", &NativeImage::CreateFromDataURL);
native_image.SetMethod("createFromNamedImage",
&NativeImage::CreateFromNamedImage);
}

} // namespace
Expand Down
3 changes: 0 additions & 3 deletions docs/api/clipboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)

In the renderer process context it depends on the [`remote`](remote.md) module on Linux,
it is therefore not available when this module is disabled.

The following example shows how to write a string to the clipboard:

```javascript
Expand Down
2 changes: 1 addition & 1 deletion filenames.gni
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ filenames = {
"lib/common/api/shell.js",
"lib/common/atom-binding-setup.ts",
"lib/common/buffer-utils.js",
"lib/common/clipboard-utils.js",
"lib/common/crash-reporter.js",
"lib/common/error-utils.js",
"lib/common/init.ts",
Expand All @@ -69,7 +70,6 @@ filenames = {
"lib/renderer/inspector.ts",
"lib/renderer/ipc-renderer-internal-utils.ts",
"lib/renderer/ipc-renderer-internal.ts",
"lib/renderer/remote.ts",
"lib/renderer/security-warnings.ts",
"lib/renderer/window-setup.ts",
"lib/renderer/web-frame-init.ts",
Expand Down
25 changes: 20 additions & 5 deletions lib/browser/rpc-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ const electron = require('electron')
const { EventEmitter } = require('events')
const fs = require('fs')
const util = require('util')

const v8Util = process.atomBinding('v8_util')
const eventBinding = process.atomBinding('event')
const clipboard = process.atomBinding('clipboard')

const { isPromise } = electron

Expand All @@ -16,6 +18,7 @@ const objectsRegistry = require('@electron/internal/browser/objects-registry')
const guestViewManager = require('@electron/internal/browser/guest-view-manager')
const bufferUtils = require('@electron/internal/common/buffer-utils')
const errorUtils = require('@electron/internal/common/error-utils')
const clipboardUtils = require('@electron/internal/common/clipboard-utils')

const hasProp = {}.hasOwnProperty

Expand Down Expand Up @@ -488,12 +491,24 @@ ipcMainUtils.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event
return event.sender.getLastWebPreferences()
})

ipcMainUtils.handle('ELECTRON_BROWSER_CLIPBOARD_READ_FIND_TEXT', function (event) {
return electron.clipboard.readFindText()
})
// Methods not listed in this set are called directly in the renderer process.
const allowedClipboardMethods = (() => {
miniak marked this conversation as resolved.
Show resolved Hide resolved
switch (process.platform) {
case 'darwin':
return new Set(['readFindText', 'writeFindText'])
case 'linux':
return new Set(Object.keys(clipboard))
default:
return new Set()
}
})()

ipcMainUtils.handle('ELECTRON_BROWSER_CLIPBOARD', function (event, method, ...args) {
if (!allowedClipboardMethods.has(method)) {
throw new Error(`Invalid method: ${method}`)
}

ipcMainUtils.handle('ELECTRON_BROWSER_CLIPBOARD_WRITE_FIND_TEXT', function (event, text) {
return electron.clipboard.writeFindText(text)
return clipboardUtils.serialize(electron.clipboard[method](...clipboardUtils.deserialize(args)))
})

const readFile = util.promisify(fs.readFile)
Expand Down
35 changes: 22 additions & 13 deletions lib/common/api/clipboard.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
'use strict'

if (process.platform === 'linux' && process.type === 'renderer') {
// On Linux we could not access clipboard in renderer process.
const { getRemote } = require('@electron/internal/renderer/remote')
module.exports = getRemote('clipboard')
} else {
const clipboard = process.atomBinding('clipboard')
const clipboard = process.atomBinding('clipboard')

// Read/write to find pasteboard over IPC since only main process is notified
// of changes
if (process.platform === 'darwin' && process.type === 'renderer') {
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils')
if (process.type === 'renderer') {
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils')
const clipboardUtils = require('@electron/internal/common/clipboard-utils')

clipboard.readFindText = (...args) => ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD_READ_FIND_TEXT', ...args)
clipboard.writeFindText = (...args) => ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD_WRITE_FIND_TEXT', ...args)
const makeRemoteMethod = function (method) {
return (...args) => {
args = clipboardUtils.serialize(args)
const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args)
return clipboardUtils.deserialize(result)
}
}

module.exports = clipboard
if (process.platform === 'linux') {
// On Linux we could not access clipboard in renderer process.
for (const method of Object.keys(clipboard)) {
clipboard[method] = makeRemoteMethod(method)
}
} else if (process.platform === 'darwin') {
// Read/write to find pasteboard over IPC since only main process is notified of changes
clipboard.readFindText = makeRemoteMethod('readFindText')
clipboard.writeFindText = makeRemoteMethod('writeFindText')
}
}

module.exports = clipboard
4 changes: 3 additions & 1 deletion lib/common/api/native-image.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'

module.exports = process.atomBinding('native_image')
const { nativeImage } = process.atomBinding('native_image')

module.exports = nativeImage
46 changes: 46 additions & 0 deletions lib/common/clipboard-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict'

const { nativeImage, NativeImage } = process.atomBinding('native_image')

const objectMap = function (source, mapper) {
const sourceEntries = Object.entries(source)
const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)])
return Object.fromEntries(targetEntries)
}

const serialize = function (value) {
if (value instanceof NativeImage) {
return {
buffer: value.toBitmap(),
size: value.getSize(),
__ELECTRON_SERIALIZED_NativeImage__: true
}
} else if (Array.isArray(value)) {
return value.map(serialize)
} else if (value instanceof Buffer) {
return value
} else if (value instanceof Object) {
return objectMap(value, serialize)
} else {
return value
}
}

const deserialize = function (value) {
if (value && value.__ELECTRON_SERIALIZED_NativeImage__) {
return nativeImage.createFromBitmap(value.buffer, value.size)
} else if (Array.isArray(value)) {
return value.map(deserialize)
} else if (value instanceof Buffer) {
return value
} else if (value instanceof Object) {
return objectMap(value, deserialize)
} else {
return value
}
}

module.exports = {
serialize,
deserialize
}
8 changes: 0 additions & 8 deletions lib/renderer/remote.ts

This file was deleted.

10 changes: 5 additions & 5 deletions lib/renderer/web-view/web-view-impl.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { deprecate, webFrame } from 'electron'
import { deprecate, remote, webFrame } from 'electron'

import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
import * as guestViewInternal from '@electron/internal/renderer/web-view/guest-view-internal'
Expand Down Expand Up @@ -221,15 +221,15 @@ export const setupAttributes = () => {
export const setupMethods = (WebViewElement: typeof ElectronInternal.WebViewElement) => {
// WebContents associated with this webview.
WebViewElement.prototype.getWebContents = function () {
const { getRemote } = require('@electron/internal/renderer/remote')
const getGuestWebContents = getRemote('getGuestWebContents')
if (!remote) {
throw new Error('getGuestWebContents requires remote, which is not enabled')
}
const internal = v8Util.getHiddenValue<WebViewImpl>(this, 'internal')

if (!internal.guestInstanceId) {
internal.createGuestSync()
}

return getGuestWebContents(internal.guestInstanceId)
return (remote as Electron.RemoteInternal).getGuestWebContents(internal.guestInstanceId!)
}

// Focusing the webview should move page focus to the underlying iframe.
Expand Down
4 changes: 4 additions & 0 deletions typings/internal-electron.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ declare namespace Electron {
sendToAll(webContentsId: number, channel: string, ...args: any[]): void
}

interface RemoteInternal extends Electron.Remote {
getGuestWebContents(guestInstanceId: number): Electron.WebContents;
}

interface WebContentsInternal extends Electron.WebContents {
_sendInternal(channel: string, ...args: any[]): void;
}
Expand Down