зеркало из https://github.com/electron/electron.git
refactor: use v8 serialization for ipc (#20214)
* refactor: use v8 serialization for ipc * cloning process.env doesn't work * serialize host objects by enumerating key/values * new serialization can handle NaN, Infinity, and undefined correctly * can't allocate v8 objects during GC * backport microtasks fix * fix compile * fix node_stream_loader reentrancy * update subframe spec to expect undefined instead of null * write undefined instead of crashing when serializing host objects * fix webview spec * fix download spec * buffers are transformed into uint8arrays * can't serialize promises * fix chrome.i18n.getMessage * fix devtools tests * fix zoom test * fix debug build * fix lint * update ipcRenderer tests * fix printToPDF test * update patch * remove accidentally re-added remote-side spec * wip * don't attempt to serialize host objects * jump through different hoops to set options.webContents sometimes * whoops * fix lint * clean up error-handling logic * fix memory leak * fix lint * convert host objects using old base::Value serialization * fix lint more * fall back to base::Value-based serialization * remove commented-out code * add docs to breaking-changes.md * Update breaking-changes.md * update ipcRenderer and WebContents docs * lint * use named values for format tag * save a memcpy for ~30% speedup * get rid of calls to ShallowClone * extra debugging for paranoia * d'oh, use the correct named tags * apparently msstl doesn't like this DCHECK * funny story about that DCHECK * disable remote-related functions when enable_remote_module = false * nits * use EnableIf to disable remote methods in mojom * fix include * review comments
This commit is contained in:
Родитель
c250cd6e7c
Коммит
2fad53e66b
|
@ -6,6 +6,59 @@ Breaking changes will be documented here, and deprecation warnings added to JS c
|
|||
|
||||
The `FIXME` string is used in code comments to denote things that should be fixed for future releases. See https://github.com/electron/electron/search?q=fixme
|
||||
|
||||
## Planned Breaking API Changes (8.0)
|
||||
|
||||
### Values sent over IPC are now serialized with Structured Clone Algorithm
|
||||
|
||||
The algorithm used to serialize objects sent over IPC (through
|
||||
`ipcRenderer.send`, `ipcRenderer.sendSync`, `WebContents.send` and related
|
||||
methods) has been switched from a custom algorithm to V8's built-in [Structured
|
||||
Clone Algorithm][SCA], the same algorithm used to serialize messages for
|
||||
`postMessage`. This brings about a 2x performance improvement for large
|
||||
messages, but also brings some breaking changes in behavior.
|
||||
|
||||
- Sending Functions, Promises, WeakMaps, WeakSets, or objects containing any
|
||||
such values, over IPC will now throw an exception, instead of silently
|
||||
converting the functions to `undefined`.
|
||||
```js
|
||||
// Previously:
|
||||
ipcRenderer.send('channel', { value: 3, someFunction: () => {} })
|
||||
// => results in { value: 3 } arriving in the main process
|
||||
|
||||
// From Electron 8:
|
||||
ipcRenderer.send('channel', { value: 3, someFunction: () => {} })
|
||||
// => throws Error("() => {} could not be cloned.")
|
||||
```
|
||||
- `NaN`, `Infinity` and `-Infinity` will now be correctly serialized, instead
|
||||
of being converted to `null`.
|
||||
- Objects containing cyclic references will now be correctly serialized,
|
||||
instead of being converted to `null`.
|
||||
- `Set`, `Map`, `Error` and `RegExp` values will be correctly serialized,
|
||||
instead of being converted to `{}`.
|
||||
- `BigInt` values will be correctly serialized, instead of being converted to
|
||||
`null`.
|
||||
- Sparse arrays will be serialized as such, instead of being converted to dense
|
||||
arrays with `null`s.
|
||||
- `Date` objects will be transferred as `Date` objects, instead of being
|
||||
converted to their ISO string representation.
|
||||
- Typed Arrays (such as `Uint8Array`, `Uint16Array`, `Uint32Array` and so on)
|
||||
will be transferred as such, instead of being converted to Node.js `Buffer`.
|
||||
- Node.js `Buffer` objects will be transferred as `Uint8Array`s. You can
|
||||
convert a `Uint8Array` back to a Node.js `Buffer` by wrapping the underlying
|
||||
`ArrayBuffer`:
|
||||
```js
|
||||
Buffer.from(value.buffer, value.byteOffset, value.byteLength)
|
||||
```
|
||||
|
||||
Sending any objects that aren't native JS types, such as DOM objects (e.g.
|
||||
`Element`, `Location`, `DOMMatrix`), Node.js objects (e.g. `process.env`,
|
||||
`Stream`), or Electron objects (e.g. `WebContents`, `BrowserWindow`,
|
||||
`WebFrame`) is deprecated. In Electron 8, these objects will be serialized as
|
||||
before with a DeprecationWarning message, but starting in Electron 9, sending
|
||||
these kinds of objects will throw a 'could not be cloned' error.
|
||||
|
||||
[SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
||||
|
||||
## Planned Breaking API Changes (7.0)
|
||||
|
||||
### Node Headers URL
|
||||
|
|
|
@ -55,9 +55,15 @@ Removes all listeners, or those of the specified `channel`.
|
|||
* `channel` String
|
||||
* `...args` any[]
|
||||
|
||||
Send a message to the main process asynchronously via `channel`, you can also
|
||||
send arbitrary arguments. Arguments will be serialized as JSON internally and
|
||||
hence no functions or prototype chain will be included.
|
||||
Send an asynchronous message to the main process via `channel`, along with
|
||||
arguments. Arguments will be serialized with the [Structured Clone
|
||||
Algorithm][SCA], just like [`postMessage`][], so prototype chains will not be
|
||||
included. Sending Functions, Promises, Symbols, WeakMaps, or WeakSets will
|
||||
throw an exception.
|
||||
|
||||
> **NOTE**: Sending non-standard JavaScript types such as DOM objects or
|
||||
> special Electron objects is deprecated, and will begin throwing an exception
|
||||
> starting with Electron 9.
|
||||
|
||||
The main process handles it by listening for `channel` with the
|
||||
[`ipcMain`](ipc-main.md) module.
|
||||
|
@ -69,9 +75,15 @@ The main process handles it by listening for `channel` with the
|
|||
|
||||
Returns `Promise<any>` - Resolves with the response from the main process.
|
||||
|
||||
Send a message to the main process asynchronously via `channel` and expect an
|
||||
asynchronous result. Arguments will be serialized as JSON internally and
|
||||
hence no functions or prototype chain will be included.
|
||||
Send a message to the main process via `channel` and expect a result
|
||||
asynchronously. Arguments will be serialized with the [Structured Clone
|
||||
Algorithm][SCA], just like [`postMessage`][], so prototype chains will not be
|
||||
included. Sending Functions, Promises, Symbols, WeakMaps, or WeakSets will
|
||||
throw an exception.
|
||||
|
||||
> **NOTE**: Sending non-standard JavaScript types such as DOM objects or
|
||||
> special Electron objects is deprecated, and will begin throwing an exception
|
||||
> starting with Electron 9.
|
||||
|
||||
The main process should listen for `channel` with
|
||||
[`ipcMain.handle()`](ipc-main.md#ipcmainhandlechannel-listener).
|
||||
|
@ -97,15 +109,23 @@ ipcMain.handle('some-name', async (event, someArgument) => {
|
|||
|
||||
Returns `any` - The value sent back by the [`ipcMain`](ipc-main.md) handler.
|
||||
|
||||
Send a message to the main process synchronously via `channel`, you can also
|
||||
send arbitrary arguments. Arguments will be serialized in JSON internally and
|
||||
hence no functions or prototype chain will be included.
|
||||
Send a message to the main process via `channel` and expect a result
|
||||
synchronously. Arguments will be serialized with the [Structured Clone
|
||||
Algorithm][SCA], just like [`postMessage`][], so prototype chains will not be
|
||||
included. Sending Functions, Promises, Symbols, WeakMaps, or WeakSets will
|
||||
throw an exception.
|
||||
|
||||
> **NOTE**: Sending non-standard JavaScript types such as DOM objects or
|
||||
> special Electron objects is deprecated, and will begin throwing an exception
|
||||
> starting with Electron 9.
|
||||
|
||||
The main process handles it by listening for `channel` with [`ipcMain`](ipc-main.md) module,
|
||||
and replies by setting `event.returnValue`.
|
||||
|
||||
**Note:** Sending a synchronous message will block the whole renderer process,
|
||||
unless you know what you are doing you should never use it.
|
||||
> :warning: **WARNING**: Sending a synchronous message will block the whole
|
||||
> renderer process until the reply is received, so use this method only as a
|
||||
> last resort. It's much better to use the asynchronous version,
|
||||
> [`invoke()`](ipc-renderer.md#ipcrendererinvokechannel-args).
|
||||
|
||||
### `ipcRenderer.sendTo(webContentsId, channel, ...args)`
|
||||
|
||||
|
@ -129,3 +149,5 @@ The documentation for the `event` object passed to the `callback` can be found
|
|||
in the [`ipc-renderer-event`](structures/ipc-renderer-event.md) structure docs.
|
||||
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
[SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
||||
[`postMessage`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
|
||||
|
|
|
@ -1479,9 +1479,15 @@ Opens the developer tools for the service worker context.
|
|||
* `channel` String
|
||||
* `...args` any[]
|
||||
|
||||
Send an asynchronous message to renderer process via `channel`, you can also
|
||||
send arbitrary arguments. Arguments will be serialized in JSON internally and
|
||||
hence no functions or prototype chain will be included.
|
||||
Send an asynchronous message to the renderer process via `channel`, along with
|
||||
arguments. Arguments will be serialized with the [Structured Clone
|
||||
Algorithm][SCA], just like [`postMessage`][], so prototype chains will not be
|
||||
included. Sending Functions, Promises, Symbols, WeakMaps, or WeakSets will
|
||||
throw an exception.
|
||||
|
||||
> **NOTE**: Sending non-standard JavaScript types such as DOM objects or
|
||||
> special Electron objects is deprecated, and will begin throwing an exception
|
||||
> starting with Electron 9.
|
||||
|
||||
The renderer process can handle the message by listening to `channel` with the
|
||||
[`ipcRenderer`](ipc-renderer.md) module.
|
||||
|
@ -1522,8 +1528,14 @@ app.on('ready', () => {
|
|||
* `...args` any[]
|
||||
|
||||
Send an asynchronous message to a specific frame in a renderer process via
|
||||
`channel`. Arguments will be serialized
|
||||
as JSON internally and as such no functions or prototype chains will be included.
|
||||
`channel`, along with arguments. Arguments will be serialized with the
|
||||
[Structured Clone Algorithm][SCA], just like [`postMessage`][], so prototype
|
||||
chains will not be included. Sending Functions, Promises, Symbols, WeakMaps, or
|
||||
WeakSets will throw an exception.
|
||||
|
||||
> **NOTE**: Sending non-standard JavaScript types such as DOM objects or
|
||||
> special Electron objects is deprecated, and will begin throwing an exception
|
||||
> starting with Electron 9.
|
||||
|
||||
The renderer process can handle the message by listening to `channel` with the
|
||||
[`ipcRenderer`](ipc-renderer.md) module.
|
||||
|
@ -1785,3 +1797,5 @@ A [`Debugger`](debugger.md) instance for this webContents.
|
|||
|
||||
[keyboardevent]: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
[SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
||||
[`postMessage`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
|
||||
|
|
|
@ -483,6 +483,7 @@ filenames = {
|
|||
"shell/common/gin_converters/net_converter.cc",
|
||||
"shell/common/gin_converters/net_converter.h",
|
||||
"shell/common/gin_converters/std_converter.h",
|
||||
"shell/common/gin_converters/blink_converter_gin_adapter.h",
|
||||
"shell/common/gin_converters/value_converter_gin_adapter.h",
|
||||
"shell/common/gin_helper/callback.cc",
|
||||
"shell/common/gin_helper/callback.h",
|
||||
|
|
|
@ -240,7 +240,7 @@ const getMessagesPath = (extensionId) => {
|
|||
|
||||
ipcMainUtils.handleSync('CHROME_GET_MESSAGES', async function (event, extensionId) {
|
||||
const messagesPath = getMessagesPath(extensionId)
|
||||
return fs.promises.readFile(messagesPath)
|
||||
return fs.promises.readFile(messagesPath, 'utf8')
|
||||
})
|
||||
|
||||
const validStorageTypes = new Set(['sync', 'local'])
|
||||
|
|
|
@ -23,7 +23,6 @@ const supportedWebViewEvents = [
|
|||
'devtools-opened',
|
||||
'devtools-closed',
|
||||
'devtools-focused',
|
||||
'new-window',
|
||||
'will-navigate',
|
||||
'did-start-navigation',
|
||||
'did-navigate',
|
||||
|
@ -48,6 +47,13 @@ const supportedWebViewEvents = [
|
|||
const guestInstances = {}
|
||||
const embedderElementsMap = {}
|
||||
|
||||
function sanitizeOptionsForGuest (options) {
|
||||
const ret = { ...options }
|
||||
// WebContents values can't be sent over IPC.
|
||||
delete ret.webContents
|
||||
return ret
|
||||
}
|
||||
|
||||
// Create a new guest instance.
|
||||
const createGuest = function (embedder, params) {
|
||||
if (webViewManager == null) {
|
||||
|
@ -114,6 +120,12 @@ const createGuest = function (embedder, params) {
|
|||
fn(event)
|
||||
}
|
||||
|
||||
guest.on('new-window', function (event, url, frameName, disposition, options, additionalFeatures, referrer) {
|
||||
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', 'new-window', url,
|
||||
frameName, disposition, sanitizeOptionsForGuest(options),
|
||||
additionalFeatures, referrer)
|
||||
})
|
||||
|
||||
// Dispatch guest's IPC messages to embedder.
|
||||
guest.on('ipc-message-host', function (_, channel, args) {
|
||||
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args)
|
||||
|
|
|
@ -250,8 +250,7 @@ ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, fra
|
|||
|
||||
// Routed window.open messages with fully parsed options
|
||||
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, referrer,
|
||||
frameName, disposition, options,
|
||||
additionalFeatures, postData) {
|
||||
frameName, disposition, options, additionalFeatures, postData) {
|
||||
options = mergeBrowserWindowOptions(event.sender, options)
|
||||
event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer)
|
||||
const { newGuest } = event
|
||||
|
|
|
@ -121,7 +121,7 @@ ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event)
|
|||
process: {
|
||||
arch: process.arch,
|
||||
platform: process.platform,
|
||||
env: process.env,
|
||||
env: { ...process.env },
|
||||
version: process.version,
|
||||
versions: process.versions,
|
||||
execPath: process.helperExecPath
|
||||
|
|
|
@ -78,4 +78,5 @@ expose_setuseragent_on_networkcontext.patch
|
|||
feat_add_set_theme_source_to_allow_apps_to.patch
|
||||
revert_cleanup_remove_menu_subtitles_sublabels.patch
|
||||
ui_views_fix_jumbo_build.patch
|
||||
export_fetchapi_mojo_traits_to_fix_component_build.patch
|
||||
fix_windows_build.patch
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jeremy Apthorp <jeremya@chromium.org>
|
||||
Date: Fri, 20 Sep 2019 16:44:18 -0400
|
||||
Subject: export FetchAPI mojo traits to fix component build
|
||||
|
||||
Without these, we get link errors in the component build when using the
|
||||
blink::CloneableMessage mojo traits.
|
||||
|
||||
diff --git a/third_party/blink/public/common/fetch/fetch_api_request_body_mojom_traits.h b/third_party/blink/public/common/fetch/fetch_api_request_body_mojom_traits.h
|
||||
index 1ddfc2108f9a0104247ea2559b597552cd20a342..f9a10b5b428e29a8824b87616f19292b38e38024 100644
|
||||
--- a/third_party/blink/public/common/fetch/fetch_api_request_body_mojom_traits.h
|
||||
+++ b/third_party/blink/public/common/fetch/fetch_api_request_body_mojom_traits.h
|
||||
@@ -11,12 +11,13 @@
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "services/network/public/cpp/resource_request_body.h"
|
||||
#include "services/network/public/mojom/url_loader.mojom-forward.h"
|
||||
+#include "third_party/blink/public/common/common_export.h"
|
||||
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-forward.h"
|
||||
|
||||
namespace mojo {
|
||||
|
||||
template <>
|
||||
-struct StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
|
||||
+struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
|
||||
scoped_refptr<network::ResourceRequestBody>> {
|
||||
static bool IsNull(const scoped_refptr<network::ResourceRequestBody>& r) {
|
||||
return !r;
|
||||
@@ -46,7 +47,7 @@ struct StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
|
||||
};
|
||||
|
||||
template <>
|
||||
-struct StructTraits<blink::mojom::FetchAPIDataElementDataView,
|
||||
+struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::FetchAPIDataElementDataView,
|
||||
network::DataElement> {
|
||||
static const network::mojom::DataElementType& type(
|
||||
const network::DataElement& element) {
|
|
@ -12,7 +12,7 @@ native_mate, and we should remove this patch once native_mate is erased
|
|||
from Electron.
|
||||
|
||||
diff --git a/gin/arguments.h b/gin/arguments.h
|
||||
index eaded13e2991..03e1495566d1 100644
|
||||
index eaded13e29919793494dfe2f7f85fad7dcb125cf..03e1495566d1ab561dcd67517053173911288cea 100644
|
||||
--- a/gin/arguments.h
|
||||
+++ b/gin/arguments.h
|
||||
@@ -28,14 +28,14 @@ class GIN_EXPORT Arguments {
|
||||
|
@ -60,7 +60,7 @@ index eaded13e2991..03e1495566d1 100644
|
|||
(is_for_property_ ? info_for_property_->GetReturnValue()
|
||||
: info_for_function_->GetReturnValue())
|
||||
diff --git a/gin/converter.h b/gin/converter.h
|
||||
index 27b4d0acd016..b19209a8534a 100644
|
||||
index 27b4d0acd016df378e4cb44ccda1a433244fe2c6..b19209a8534a497373c5a2f861b26502e96144c9 100644
|
||||
--- a/gin/converter.h
|
||||
+++ b/gin/converter.h
|
||||
@@ -250,7 +250,7 @@ std::enable_if_t<ToV8ReturnsMaybe<T>::value, bool> TryConvertToV8(
|
||||
|
|
|
@ -1017,7 +1017,7 @@ void WebContents::OnElectronBrowserConnectionError() {
|
|||
|
||||
void WebContents::Message(bool internal,
|
||||
const std::string& channel,
|
||||
base::Value arguments) {
|
||||
blink::CloneableMessage arguments) {
|
||||
// webContents.emit('-ipc-message', new Event(), internal, channel,
|
||||
// arguments);
|
||||
EmitWithSender("-ipc-message", bindings_.dispatch_context(), base::nullopt,
|
||||
|
@ -1026,7 +1026,7 @@ void WebContents::Message(bool internal,
|
|||
|
||||
void WebContents::Invoke(bool internal,
|
||||
const std::string& channel,
|
||||
base::Value arguments,
|
||||
blink::CloneableMessage arguments,
|
||||
InvokeCallback callback) {
|
||||
// webContents.emit('-ipc-invoke', new Event(), internal, channel, arguments);
|
||||
EmitWithSender("-ipc-invoke", bindings_.dispatch_context(),
|
||||
|
@ -1035,7 +1035,7 @@ void WebContents::Invoke(bool internal,
|
|||
|
||||
void WebContents::MessageSync(bool internal,
|
||||
const std::string& channel,
|
||||
base::Value arguments,
|
||||
blink::CloneableMessage arguments,
|
||||
MessageSyncCallback callback) {
|
||||
// webContents.emit('-ipc-message-sync', new Event(sender, message), internal,
|
||||
// channel, arguments);
|
||||
|
@ -1047,24 +1047,37 @@ void WebContents::MessageTo(bool internal,
|
|||
bool send_to_all,
|
||||
int32_t web_contents_id,
|
||||
const std::string& channel,
|
||||
base::Value arguments) {
|
||||
blink::CloneableMessage arguments) {
|
||||
auto* web_contents = mate::TrackableObject<WebContents>::FromWeakMapID(
|
||||
isolate(), web_contents_id);
|
||||
|
||||
if (web_contents) {
|
||||
web_contents->SendIPCMessageWithSender(internal, send_to_all, channel,
|
||||
base::ListValue(arguments.GetList()),
|
||||
ID());
|
||||
std::move(arguments), ID());
|
||||
}
|
||||
}
|
||||
|
||||
void WebContents::MessageHost(const std::string& channel,
|
||||
base::Value arguments) {
|
||||
blink::CloneableMessage arguments) {
|
||||
// webContents.emit('ipc-message-host', new Event(), channel, args);
|
||||
EmitWithSender("ipc-message-host", bindings_.dispatch_context(),
|
||||
base::nullopt, channel, std::move(arguments));
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
|
||||
void WebContents::DereferenceRemoteJSObject(const std::string& context_id,
|
||||
int object_id,
|
||||
int ref_count) {
|
||||
base::ListValue args;
|
||||
args.Append(context_id);
|
||||
args.Append(object_id);
|
||||
args.Append(ref_count);
|
||||
EmitWithSender("-ipc-message", bindings_.dispatch_context(), base::nullopt,
|
||||
/* internal */ true, "ELECTRON_BROWSER_DEREFERENCE",
|
||||
std::move(args));
|
||||
}
|
||||
#endif
|
||||
|
||||
void WebContents::UpdateDraggableRegions(
|
||||
std::vector<mojom::DraggableRegionPtr> regions) {
|
||||
for (ExtendedWebContentsObserver& observer : observers_)
|
||||
|
@ -1986,14 +1999,21 @@ void WebContents::TabTraverse(bool reverse) {
|
|||
bool WebContents::SendIPCMessage(bool internal,
|
||||
bool send_to_all,
|
||||
const std::string& channel,
|
||||
const base::ListValue& args) {
|
||||
return SendIPCMessageWithSender(internal, send_to_all, channel, args);
|
||||
v8::Local<v8::Value> args) {
|
||||
blink::CloneableMessage message;
|
||||
if (!mate::ConvertFromV8(isolate(), args, &message)) {
|
||||
isolate()->ThrowException(v8::Exception::Error(
|
||||
mate::StringToV8(isolate(), "Failed to serialize arguments")));
|
||||
return false;
|
||||
}
|
||||
return SendIPCMessageWithSender(internal, send_to_all, channel,
|
||||
std::move(message));
|
||||
}
|
||||
|
||||
bool WebContents::SendIPCMessageWithSender(bool internal,
|
||||
bool send_to_all,
|
||||
const std::string& channel,
|
||||
const base::ListValue& args,
|
||||
blink::CloneableMessage args,
|
||||
int32_t sender_id) {
|
||||
std::vector<content::RenderFrameHost*> target_hosts;
|
||||
if (!send_to_all) {
|
||||
|
@ -2009,7 +2029,7 @@ bool WebContents::SendIPCMessageWithSender(bool internal,
|
|||
mojom::ElectronRendererAssociatedPtr electron_ptr;
|
||||
frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
|
||||
mojo::MakeRequest(&electron_ptr));
|
||||
electron_ptr->Message(internal, false, channel, args.Clone(), sender_id);
|
||||
electron_ptr->Message(internal, false, channel, std::move(args), sender_id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -2018,7 +2038,13 @@ bool WebContents::SendIPCMessageToFrame(bool internal,
|
|||
bool send_to_all,
|
||||
int32_t frame_id,
|
||||
const std::string& channel,
|
||||
const base::ListValue& args) {
|
||||
v8::Local<v8::Value> args) {
|
||||
blink::CloneableMessage message;
|
||||
if (!mate::ConvertFromV8(isolate(), args, &message)) {
|
||||
isolate()->ThrowException(v8::Exception::Error(
|
||||
mate::StringToV8(isolate(), "Failed to serialize arguments")));
|
||||
return false;
|
||||
}
|
||||
auto frames = web_contents()->GetAllFrames();
|
||||
auto iter = std::find_if(frames.begin(), frames.end(), [frame_id](auto* f) {
|
||||
return f->GetRoutingID() == frame_id;
|
||||
|
@ -2031,7 +2057,7 @@ bool WebContents::SendIPCMessageToFrame(bool internal,
|
|||
mojom::ElectronRendererAssociatedPtr electron_ptr;
|
||||
(*iter)->GetRemoteAssociatedInterfaces()->GetInterface(
|
||||
mojo::MakeRequest(&electron_ptr));
|
||||
electron_ptr->Message(internal, send_to_all, channel, args.Clone(),
|
||||
electron_ptr->Message(internal, send_to_all, channel, std::move(message),
|
||||
0 /* sender_id */);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -220,19 +220,19 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
bool SendIPCMessage(bool internal,
|
||||
bool send_to_all,
|
||||
const std::string& channel,
|
||||
const base::ListValue& args);
|
||||
v8::Local<v8::Value> args);
|
||||
|
||||
bool SendIPCMessageWithSender(bool internal,
|
||||
bool send_to_all,
|
||||
const std::string& channel,
|
||||
const base::ListValue& args,
|
||||
blink::CloneableMessage args,
|
||||
int32_t sender_id = 0);
|
||||
|
||||
bool SendIPCMessageToFrame(bool internal,
|
||||
bool send_to_all,
|
||||
int32_t frame_id,
|
||||
const std::string& channel,
|
||||
const base::ListValue& args);
|
||||
v8::Local<v8::Value> args);
|
||||
|
||||
// Send WebInputEvent to the page.
|
||||
void SendInputEvent(v8::Isolate* isolate, v8::Local<v8::Value> input_event);
|
||||
|
@ -491,21 +491,27 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
// mojom::ElectronBrowser
|
||||
void Message(bool internal,
|
||||
const std::string& channel,
|
||||
base::Value arguments) override;
|
||||
blink::CloneableMessage arguments) override;
|
||||
void Invoke(bool internal,
|
||||
const std::string& channel,
|
||||
base::Value arguments,
|
||||
blink::CloneableMessage arguments,
|
||||
InvokeCallback callback) override;
|
||||
void MessageSync(bool internal,
|
||||
const std::string& channel,
|
||||
base::Value arguments,
|
||||
blink::CloneableMessage arguments,
|
||||
MessageSyncCallback callback) override;
|
||||
void MessageTo(bool internal,
|
||||
bool send_to_all,
|
||||
int32_t web_contents_id,
|
||||
const std::string& channel,
|
||||
base::Value arguments) override;
|
||||
void MessageHost(const std::string& channel, base::Value arguments) override;
|
||||
blink::CloneableMessage arguments) override;
|
||||
void MessageHost(const std::string& channel,
|
||||
blink::CloneableMessage arguments) override;
|
||||
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
|
||||
void DereferenceRemoteJSObject(const std::string& context_id,
|
||||
int object_id,
|
||||
int ref_count) override;
|
||||
#endif
|
||||
void UpdateDraggableRegions(
|
||||
std::vector<mojom::DraggableRegionPtr> regions) override;
|
||||
void SetTemporaryZoomLevel(double level) override;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include "native_mate/object_template_builder_deprecated.h"
|
||||
#include "shell/common/native_mate_converters/value_converter.h"
|
||||
#include "shell/common/native_mate_converters/blink_converter.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
|
@ -17,7 +17,7 @@ Event::Event(v8::Isolate* isolate) {
|
|||
|
||||
Event::~Event() = default;
|
||||
|
||||
void Event::SetCallback(base::Optional<MessageSyncCallback> callback) {
|
||||
void Event::SetCallback(base::Optional<InvokeCallback> callback) {
|
||||
DCHECK(!callback_);
|
||||
callback_ = std::move(callback);
|
||||
}
|
||||
|
@ -29,11 +29,16 @@ void Event::PreventDefault(v8::Isolate* isolate) {
|
|||
.Check();
|
||||
}
|
||||
|
||||
bool Event::SendReply(const base::Value& result) {
|
||||
bool Event::SendReply(v8::Isolate* isolate, v8::Local<v8::Value> result) {
|
||||
if (!callback_)
|
||||
return false;
|
||||
|
||||
std::move(*callback_).Run(result.Clone());
|
||||
blink::CloneableMessage message;
|
||||
if (!ConvertFromV8(isolate, result, &message)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::move(*callback_).Run(std::move(message));
|
||||
callback_.reset();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -18,22 +18,21 @@ namespace mate {
|
|||
|
||||
class Event : public Wrappable<Event> {
|
||||
public:
|
||||
using MessageSyncCallback =
|
||||
electron::mojom::ElectronBrowser::MessageSyncCallback;
|
||||
using InvokeCallback = electron::mojom::ElectronBrowser::InvokeCallback;
|
||||
static Handle<Event> Create(v8::Isolate* isolate);
|
||||
|
||||
static void BuildPrototype(v8::Isolate* isolate,
|
||||
v8::Local<v8::FunctionTemplate> prototype);
|
||||
|
||||
// Pass the callback to be invoked.
|
||||
void SetCallback(base::Optional<MessageSyncCallback> callback);
|
||||
void SetCallback(base::Optional<InvokeCallback> callback);
|
||||
|
||||
// event.PreventDefault().
|
||||
void PreventDefault(v8::Isolate* isolate);
|
||||
|
||||
// event.sendReply(value), used for replying to synchronous messages and
|
||||
// `invoke` calls.
|
||||
bool SendReply(const base::Value& result);
|
||||
bool SendReply(v8::Isolate* isolate, v8::Local<v8::Value> result);
|
||||
|
||||
protected:
|
||||
explicit Event(v8::Isolate* isolate);
|
||||
|
@ -41,7 +40,7 @@ class Event : public Wrappable<Event> {
|
|||
|
||||
private:
|
||||
// Replyer for the synchronous messages.
|
||||
base::Optional<MessageSyncCallback> callback_;
|
||||
base::Optional<InvokeCallback> callback_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Event);
|
||||
};
|
||||
|
|
|
@ -82,8 +82,7 @@ class EventEmitter : public Wrappable<T> {
|
|||
bool EmitWithSender(
|
||||
base::StringPiece name,
|
||||
content::RenderFrameHost* sender,
|
||||
base::Optional<electron::mojom::ElectronBrowser::MessageSyncCallback>
|
||||
callback,
|
||||
base::Optional<electron::mojom::ElectronBrowser::InvokeCallback> callback,
|
||||
Args&&... args) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
v8::Locker locker(isolate());
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import("//mojo/public/tools/bindings/mojom.gni")
|
||||
import("../../../buildflags/buildflags.gni")
|
||||
|
||||
mojom("mojo") {
|
||||
sources = [
|
||||
|
@ -7,6 +8,12 @@ mojom("mojo") {
|
|||
|
||||
public_deps = [
|
||||
"//mojo/public/mojom/base",
|
||||
"//third_party/blink/public/mojom:mojom_core",
|
||||
"//ui/gfx/geometry/mojom",
|
||||
]
|
||||
|
||||
enabled_features = []
|
||||
if (enable_remote_module) {
|
||||
enabled_features += [ "enable_remote_module" ]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
module electron.mojom;
|
||||
|
||||
import "mojo/public/mojom/base/values.mojom";
|
||||
import "mojo/public/mojom/base/string16.mojom";
|
||||
import "ui/gfx/geometry/mojom/geometry.mojom";
|
||||
import "third_party/blink/public/mojom/messaging/cloneable_message.mojom";
|
||||
|
||||
interface ElectronRenderer {
|
||||
Message(
|
||||
bool internal,
|
||||
bool send_to_all,
|
||||
string channel,
|
||||
mojo_base.mojom.ListValue arguments,
|
||||
blink.mojom.CloneableMessage arguments,
|
||||
int32 sender_id);
|
||||
|
||||
UpdateCrashpadPipeName(string pipe_name);
|
||||
|
||||
// This is an API specific to the "remote" module, and will ultimately be
|
||||
// replaced by generic IPC once WeakRef is generally available.
|
||||
[EnableIf=enable_remote_module]
|
||||
DereferenceRemoteJSCallback(
|
||||
string context_id,
|
||||
int32 object_id);
|
||||
|
||||
TakeHeapSnapshot(handle file) => (bool success);
|
||||
};
|
||||
|
||||
|
@ -37,14 +44,14 @@ interface ElectronBrowser {
|
|||
Message(
|
||||
bool internal,
|
||||
string channel,
|
||||
mojo_base.mojom.ListValue arguments);
|
||||
blink.mojom.CloneableMessage arguments);
|
||||
|
||||
// Emits an event on |channel| from the ipcMain JavaScript object in the main
|
||||
// process, and returns the response.
|
||||
Invoke(
|
||||
bool internal,
|
||||
string channel,
|
||||
mojo_base.mojom.ListValue arguments) => (mojo_base.mojom.Value result);
|
||||
blink.mojom.CloneableMessage arguments) => (blink.mojom.CloneableMessage result);
|
||||
|
||||
// Emits an event on |channel| from the ipcMain JavaScript object in the main
|
||||
// process, and waits synchronously for a response.
|
||||
|
@ -55,7 +62,7 @@ interface ElectronBrowser {
|
|||
MessageSync(
|
||||
bool internal,
|
||||
string channel,
|
||||
mojo_base.mojom.ListValue arguments) => (mojo_base.mojom.Value result);
|
||||
blink.mojom.CloneableMessage arguments) => (blink.mojom.CloneableMessage result);
|
||||
|
||||
// Emits an event from the |ipcRenderer| JavaScript object in the target
|
||||
// WebContents's main frame, specified by |web_contents_id|.
|
||||
|
@ -64,11 +71,19 @@ interface ElectronBrowser {
|
|||
bool send_to_all,
|
||||
int32 web_contents_id,
|
||||
string channel,
|
||||
mojo_base.mojom.ListValue arguments);
|
||||
blink.mojom.CloneableMessage arguments);
|
||||
|
||||
MessageHost(
|
||||
string channel,
|
||||
mojo_base.mojom.ListValue arguments);
|
||||
blink.mojom.CloneableMessage arguments);
|
||||
|
||||
// This is an API specific to the "remote" module, and will ultimately be
|
||||
// replaced by generic IPC once WeakRef is generally available.
|
||||
[EnableIf=enable_remote_module]
|
||||
DereferenceRemoteJSObject(
|
||||
string context_id,
|
||||
int32 object_id,
|
||||
int32 ref_count);
|
||||
|
||||
UpdateDraggableRegions(
|
||||
array<DraggableRegion> regions);
|
||||
|
|
|
@ -35,18 +35,12 @@ RemoteCallbackFreer::RemoteCallbackFreer(v8::Isolate* isolate,
|
|||
RemoteCallbackFreer::~RemoteCallbackFreer() = default;
|
||||
|
||||
void RemoteCallbackFreer::RunDestructor() {
|
||||
auto* channel = "ELECTRON_RENDERER_RELEASE_CALLBACK";
|
||||
base::ListValue args;
|
||||
int32_t sender_id = 0;
|
||||
args.AppendString(context_id_);
|
||||
args.AppendInteger(object_id_);
|
||||
auto* frame_host = web_contents()->GetMainFrame();
|
||||
if (frame_host) {
|
||||
mojom::ElectronRendererAssociatedPtr electron_ptr;
|
||||
frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
|
||||
mojo::MakeRequest(&electron_ptr));
|
||||
electron_ptr->Message(true /* internal */, false /* send_to_all */, channel,
|
||||
args.Clone(), sender_id);
|
||||
electron_ptr->DereferenceRemoteJSCallback(context_id_, object_id_);
|
||||
}
|
||||
|
||||
Observe(nullptr);
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "base/values.h"
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
#include "electron/shell/common/api/api.mojom.h"
|
||||
#include "electron/shell/common/native_mate_converters/blink_converter.h"
|
||||
#include "electron/shell/common/native_mate_converters/value_converter.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
|
||||
#include "third_party/blink/public/web/web_local_frame.h"
|
||||
|
||||
|
@ -80,17 +82,10 @@ void RemoteObjectFreer::RunDestructor() {
|
|||
ref_mapper_.erase(objects_it);
|
||||
}
|
||||
|
||||
auto* channel = "ELECTRON_BROWSER_DEREFERENCE";
|
||||
|
||||
base::ListValue args;
|
||||
args.AppendString(context_id_);
|
||||
args.AppendInteger(object_id_);
|
||||
args.AppendInteger(ref_count);
|
||||
|
||||
mojom::ElectronBrowserAssociatedPtr electron_ptr;
|
||||
render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
|
||||
mojo::MakeRequest(&electron_ptr));
|
||||
electron_ptr->Message(true, channel, args.Clone());
|
||||
electron_ptr->DereferenceRemoteJSObject(context_id_, object_id_, ref_count);
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_COMMON_GIN_CONVERTERS_BLINK_CONVERTER_GIN_ADAPTER_H_
|
||||
#define SHELL_COMMON_GIN_CONVERTERS_BLINK_CONVERTER_GIN_ADAPTER_H_
|
||||
|
||||
#include "gin/converter.h"
|
||||
#include "shell/common/native_mate_converters/blink_converter.h"
|
||||
|
||||
// TODO(zcbenz): Move the implementations from native_mate_converters to here.
|
||||
|
||||
namespace gin {
|
||||
|
||||
template <>
|
||||
struct Converter<blink::CloneableMessage> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
blink::CloneableMessage* out) {
|
||||
return mate::ConvertFromV8(isolate, val, out);
|
||||
}
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const blink::CloneableMessage& val) {
|
||||
return mate::ConvertToV8(isolate, val);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gin
|
||||
|
||||
#endif // SHELL_COMMON_GIN_CONVERTERS_BLINK_CONVERTER_GIN_ADAPTER_H_
|
|
@ -6,14 +6,19 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "content/public/browser/native_web_keyboard_event.h"
|
||||
#include "gin/converter.h"
|
||||
#include "mojo/public/cpp/base/values_mojom_traits.h"
|
||||
#include "mojo/public/mojom/base/values.mojom.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "shell/common/deprecate_util.h"
|
||||
#include "shell/common/keyboard_util.h"
|
||||
#include "shell/common/native_mate_converters/value_converter.h"
|
||||
#include "third_party/blink/public/platform/web_input_event.h"
|
||||
#include "third_party/blink/public/platform/web_mouse_event.h"
|
||||
#include "third_party/blink/public/platform/web_mouse_wheel_event.h"
|
||||
|
@ -527,4 +532,184 @@ bool Converter<network::mojom::ReferrerPolicy>::FromV8(
|
|||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t kNewSerializationTag = 0;
|
||||
constexpr uint8_t kOldSerializationTag = 1;
|
||||
|
||||
class V8Serializer : public v8::ValueSerializer::Delegate {
|
||||
public:
|
||||
explicit V8Serializer(v8::Isolate* isolate,
|
||||
bool use_old_serialization = false)
|
||||
: isolate_(isolate),
|
||||
serializer_(isolate, this),
|
||||
use_old_serialization_(use_old_serialization) {}
|
||||
~V8Serializer() override = default;
|
||||
|
||||
bool Serialize(v8::Local<v8::Value> value, blink::CloneableMessage* out) {
|
||||
serializer_.WriteHeader();
|
||||
if (use_old_serialization_) {
|
||||
WriteTag(kOldSerializationTag);
|
||||
if (!WriteBaseValue(value)) {
|
||||
isolate_->ThrowException(
|
||||
mate::StringToV8(isolate_, "An object could not be cloned."));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
WriteTag(kNewSerializationTag);
|
||||
bool wrote_value;
|
||||
v8::TryCatch try_catch(isolate_);
|
||||
if (!serializer_.WriteValue(isolate_->GetCurrentContext(), value)
|
||||
.To(&wrote_value)) {
|
||||
try_catch.Reset();
|
||||
if (!V8Serializer(isolate_, true).Serialize(value, out)) {
|
||||
try_catch.ReThrow();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
DCHECK(wrote_value);
|
||||
}
|
||||
|
||||
std::pair<uint8_t*, size_t> buffer = serializer_.Release();
|
||||
DCHECK_EQ(buffer.first, data_.data());
|
||||
out->encoded_message = base::make_span(buffer.first, buffer.second);
|
||||
out->owned_encoded_message = std::move(data_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteBaseValue(v8::Local<v8::Value> object) {
|
||||
node::Environment* env = node::Environment::GetCurrent(isolate_);
|
||||
if (env) {
|
||||
electron::EmitDeprecationWarning(
|
||||
env,
|
||||
"Passing functions, DOM objects and other non-cloneable JavaScript "
|
||||
"objects to IPC methods is deprecated and will throw an exception "
|
||||
"beginning with Electron 9.",
|
||||
"DeprecationWarning");
|
||||
}
|
||||
base::Value value;
|
||||
if (!ConvertFromV8(isolate_, object, &value)) {
|
||||
return false;
|
||||
}
|
||||
mojo::Message message = mojo_base::mojom::Value::SerializeAsMessage(&value);
|
||||
|
||||
serializer_.WriteUint32(message.data_num_bytes());
|
||||
serializer_.WriteRawBytes(message.data(), message.data_num_bytes());
|
||||
return true;
|
||||
}
|
||||
|
||||
void WriteTag(uint8_t tag) { serializer_.WriteRawBytes(&tag, 1); }
|
||||
|
||||
// v8::ValueSerializer::Delegate
|
||||
void* ReallocateBufferMemory(void* old_buffer,
|
||||
size_t size,
|
||||
size_t* actual_size) override {
|
||||
DCHECK_EQ(old_buffer, data_.data());
|
||||
data_.resize(size);
|
||||
*actual_size = data_.capacity();
|
||||
return data_.data();
|
||||
}
|
||||
|
||||
void FreeBufferMemory(void* buffer) override {
|
||||
DCHECK_EQ(buffer, data_.data());
|
||||
data_ = {};
|
||||
}
|
||||
|
||||
void ThrowDataCloneError(v8::Local<v8::String> message) override {
|
||||
isolate_->ThrowException(v8::Exception::Error(message));
|
||||
}
|
||||
|
||||
private:
|
||||
v8::Isolate* isolate_;
|
||||
std::vector<uint8_t> data_;
|
||||
v8::ValueSerializer serializer_;
|
||||
bool use_old_serialization_;
|
||||
};
|
||||
|
||||
class V8Deserializer : public v8::ValueDeserializer::Delegate {
|
||||
public:
|
||||
V8Deserializer(v8::Isolate* isolate, const blink::CloneableMessage& message)
|
||||
: isolate_(isolate),
|
||||
deserializer_(isolate,
|
||||
message.encoded_message.data(),
|
||||
message.encoded_message.size(),
|
||||
this) {}
|
||||
|
||||
v8::Local<v8::Value> Deserialize() {
|
||||
v8::EscapableHandleScope scope(isolate_);
|
||||
auto context = isolate_->GetCurrentContext();
|
||||
bool read_header;
|
||||
if (!deserializer_.ReadHeader(context).To(&read_header))
|
||||
return v8::Null(isolate_);
|
||||
DCHECK(read_header);
|
||||
uint8_t tag;
|
||||
if (!ReadTag(&tag))
|
||||
return v8::Null(isolate_);
|
||||
switch (tag) {
|
||||
case kNewSerializationTag: {
|
||||
v8::Local<v8::Value> value;
|
||||
if (!deserializer_.ReadValue(context).ToLocal(&value)) {
|
||||
return v8::Null(isolate_);
|
||||
}
|
||||
return scope.Escape(value);
|
||||
}
|
||||
case kOldSerializationTag: {
|
||||
v8::Local<v8::Value> value;
|
||||
if (!ReadBaseValue(&value)) {
|
||||
return v8::Null(isolate_);
|
||||
}
|
||||
return scope.Escape(value);
|
||||
}
|
||||
default:
|
||||
NOTREACHED() << "Invalid tag: " << tag;
|
||||
return v8::Null(isolate_);
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadTag(uint8_t* tag) {
|
||||
const void* tag_bytes;
|
||||
if (!deserializer_.ReadRawBytes(1, &tag_bytes))
|
||||
return false;
|
||||
*tag = *reinterpret_cast<const uint8_t*>(tag_bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadBaseValue(v8::Local<v8::Value>* value) {
|
||||
uint32_t length;
|
||||
const void* data;
|
||||
if (!deserializer_.ReadUint32(&length) ||
|
||||
!deserializer_.ReadRawBytes(length, &data)) {
|
||||
return false;
|
||||
}
|
||||
mojo::Message message(
|
||||
base::make_span(reinterpret_cast<const uint8_t*>(data), length), {});
|
||||
base::Value out;
|
||||
if (!mojo_base::mojom::Value::DeserializeFromMessage(std::move(message),
|
||||
&out)) {
|
||||
return false;
|
||||
}
|
||||
*value = ConvertToV8(isolate_, out);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
v8::Isolate* isolate_;
|
||||
v8::ValueDeserializer deserializer_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
v8::Local<v8::Value> Converter<blink::CloneableMessage>::ToV8(
|
||||
v8::Isolate* isolate,
|
||||
const blink::CloneableMessage& in) {
|
||||
return V8Deserializer(isolate, in).Deserialize();
|
||||
}
|
||||
|
||||
bool Converter<blink::CloneableMessage>::FromV8(v8::Isolate* isolate,
|
||||
v8::Handle<v8::Value> val,
|
||||
blink::CloneableMessage* out) {
|
||||
return V8Serializer(isolate).Serialize(val, out);
|
||||
}
|
||||
|
||||
} // namespace mate
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define SHELL_COMMON_NATIVE_MATE_CONVERTERS_BLINK_CONVERTER_H_
|
||||
|
||||
#include "native_mate/converter.h"
|
||||
#include "third_party/blink/public/common/messaging/cloneable_message.h"
|
||||
#include "third_party/blink/public/platform/web_cache.h"
|
||||
#include "third_party/blink/public/platform/web_input_event.h"
|
||||
#include "third_party/blink/public/web/web_context_menu_data.h"
|
||||
|
@ -131,6 +132,15 @@ struct Converter<network::mojom::ReferrerPolicy> {
|
|||
network::mojom::ReferrerPolicy* out);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<blink::CloneableMessage> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const blink::CloneableMessage& in);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
blink::CloneableMessage* out);
|
||||
};
|
||||
|
||||
v8::Local<v8::Value> EditFlagsToV8(v8::Isolate* isolate, int editFlags);
|
||||
v8::Local<v8::Value> MediaFlagsToV8(v8::Isolate* isolate, int mediaFlags);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "gin/wrappable.h"
|
||||
#include "services/service_manager/public/cpp/interface_provider.h"
|
||||
#include "shell/common/api/api.mojom.h"
|
||||
#include "shell/common/gin_converters/blink_converter_gin_adapter.h"
|
||||
#include "shell/common/gin_converters/value_converter_gin_adapter.h"
|
||||
#include "shell/common/node_bindings.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
|
@ -69,45 +70,71 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer> {
|
|||
const char* GetTypeName() override { return "IPCRenderer"; }
|
||||
|
||||
private:
|
||||
void Send(bool internal,
|
||||
void Send(v8::Isolate* isolate,
|
||||
bool internal,
|
||||
const std::string& channel,
|
||||
const base::ListValue& arguments) {
|
||||
electron_browser_ptr_->get()->Message(internal, channel, arguments.Clone());
|
||||
v8::Local<v8::Value> arguments) {
|
||||
blink::CloneableMessage message;
|
||||
if (!mate::ConvertFromV8(isolate, arguments, &message)) {
|
||||
return;
|
||||
}
|
||||
electron_browser_ptr_->get()->Message(internal, channel,
|
||||
std::move(message));
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> Invoke(v8::Isolate* isolate,
|
||||
bool internal,
|
||||
const std::string& channel,
|
||||
const base::Value& arguments) {
|
||||
electron::util::Promise<base::Value> p(isolate);
|
||||
v8::Local<v8::Value> arguments) {
|
||||
blink::CloneableMessage message;
|
||||
if (!mate::ConvertFromV8(isolate, arguments, &message)) {
|
||||
return v8::Local<v8::Promise>();
|
||||
}
|
||||
electron::util::Promise<blink::CloneableMessage> p(isolate);
|
||||
auto handle = p.GetHandle();
|
||||
|
||||
electron_browser_ptr_->get()->Invoke(
|
||||
internal, channel, arguments.Clone(),
|
||||
base::BindOnce([](electron::util::Promise<base::Value> p,
|
||||
base::Value result) { p.ResolveWithGin(result); },
|
||||
std::move(p)));
|
||||
internal, channel, std::move(message),
|
||||
base::BindOnce(
|
||||
[](electron::util::Promise<blink::CloneableMessage> p,
|
||||
blink::CloneableMessage result) { p.ResolveWithGin(result); },
|
||||
std::move(p)));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void SendTo(bool internal,
|
||||
void SendTo(v8::Isolate* isolate,
|
||||
bool internal,
|
||||
bool send_to_all,
|
||||
int32_t web_contents_id,
|
||||
const std::string& channel,
|
||||
const base::ListValue& arguments) {
|
||||
v8::Local<v8::Value> arguments) {
|
||||
blink::CloneableMessage message;
|
||||
if (!mate::ConvertFromV8(isolate, arguments, &message)) {
|
||||
return;
|
||||
}
|
||||
electron_browser_ptr_->get()->MessageTo(
|
||||
internal, send_to_all, web_contents_id, channel, arguments.Clone());
|
||||
internal, send_to_all, web_contents_id, channel, std::move(message));
|
||||
}
|
||||
|
||||
void SendToHost(const std::string& channel,
|
||||
const base::ListValue& arguments) {
|
||||
electron_browser_ptr_->get()->MessageHost(channel, arguments.Clone());
|
||||
void SendToHost(v8::Isolate* isolate,
|
||||
const std::string& channel,
|
||||
v8::Local<v8::Value> arguments) {
|
||||
blink::CloneableMessage message;
|
||||
if (!mate::ConvertFromV8(isolate, arguments, &message)) {
|
||||
return;
|
||||
}
|
||||
electron_browser_ptr_->get()->MessageHost(channel, std::move(message));
|
||||
}
|
||||
|
||||
base::Value SendSync(bool internal,
|
||||
const std::string& channel,
|
||||
const base::ListValue& arguments) {
|
||||
blink::CloneableMessage SendSync(v8::Isolate* isolate,
|
||||
bool internal,
|
||||
const std::string& channel,
|
||||
v8::Local<v8::Value> arguments) {
|
||||
blink::CloneableMessage message;
|
||||
if (!mate::ConvertFromV8(isolate, arguments, &message)) {
|
||||
return blink::CloneableMessage();
|
||||
}
|
||||
// We aren't using a true synchronous mojo call here. We're calling an
|
||||
// asynchronous method and blocking on the result. The reason we're doing
|
||||
// this is a little complicated, so buckle up.
|
||||
|
@ -154,7 +181,7 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer> {
|
|||
//
|
||||
// Phew. If you got this far, here's a gold star: ⭐️
|
||||
|
||||
base::Value result;
|
||||
blink::CloneableMessage result;
|
||||
|
||||
// A task is posted to a worker thread to execute the request so that
|
||||
// this thread may block on a waitable event. It is safe to pass raw
|
||||
|
@ -167,16 +194,16 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer> {
|
|||
base::Unretained(this),
|
||||
base::Unretained(&response_received_event),
|
||||
base::Unretained(&result), internal, channel,
|
||||
arguments.Clone()));
|
||||
std::move(message)));
|
||||
response_received_event.Wait();
|
||||
return result;
|
||||
}
|
||||
|
||||
void SendMessageSyncOnWorkerThread(base::WaitableEvent* event,
|
||||
base::Value* result,
|
||||
blink::CloneableMessage* result,
|
||||
bool internal,
|
||||
const std::string& channel,
|
||||
base::Value arguments) {
|
||||
blink::CloneableMessage arguments) {
|
||||
electron_browser_ptr_->get()->MessageSync(
|
||||
internal, channel, std::move(arguments),
|
||||
base::BindOnce(&IPCRenderer::ReturnSyncResponseToMainThread,
|
||||
|
@ -184,8 +211,8 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer> {
|
|||
}
|
||||
|
||||
static void ReturnSyncResponseToMainThread(base::WaitableEvent* event,
|
||||
base::Value* result,
|
||||
base::Value response) {
|
||||
blink::CloneableMessage* result,
|
||||
blink::CloneableMessage response) {
|
||||
*result = std::move(response);
|
||||
event->Signal();
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "base/threading/thread_restrictions.h"
|
||||
#include "mojo/public/cpp/system/platform_handle.h"
|
||||
#include "shell/common/atom_constants.h"
|
||||
#include "shell/common/gin_converters/blink_converter_gin_adapter.h"
|
||||
#include "shell/common/gin_converters/value_converter_gin_adapter.h"
|
||||
#include "shell/common/heap_snapshot.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
|
@ -73,7 +74,7 @@ void InvokeIpcCallback(v8::Local<v8::Context> context,
|
|||
void EmitIPCEvent(v8::Local<v8::Context> context,
|
||||
bool internal,
|
||||
const std::string& channel,
|
||||
const std::vector<base::Value>& args,
|
||||
v8::Local<v8::Value> args,
|
||||
int32_t sender_id) {
|
||||
auto* isolate = context->GetIsolate();
|
||||
|
||||
|
@ -84,7 +85,7 @@ void EmitIPCEvent(v8::Local<v8::Context> context,
|
|||
|
||||
std::vector<v8::Local<v8::Value>> argv = {
|
||||
gin::ConvertToV8(isolate, internal), gin::ConvertToV8(isolate, channel),
|
||||
gin::ConvertToV8(isolate, args), gin::ConvertToV8(isolate, sender_id)};
|
||||
args, gin::ConvertToV8(isolate, sender_id)};
|
||||
|
||||
InvokeIpcCallback(context, "onMessage", argv);
|
||||
}
|
||||
|
@ -128,7 +129,7 @@ void ElectronApiServiceImpl::OnConnectionError() {
|
|||
void ElectronApiServiceImpl::Message(bool internal,
|
||||
bool send_to_all,
|
||||
const std::string& channel,
|
||||
base::Value arguments,
|
||||
blink::CloneableMessage arguments,
|
||||
int32_t sender_id) {
|
||||
// Don't handle browser messages before document element is created.
|
||||
//
|
||||
|
@ -157,8 +158,11 @@ void ElectronApiServiceImpl::Message(bool internal,
|
|||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
v8::Local<v8::Context> context = renderer_client_->GetContext(frame, isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
EmitIPCEvent(context, internal, channel, arguments.GetList(), sender_id);
|
||||
v8::Local<v8::Value> args = gin::ConvertToV8(isolate, arguments);
|
||||
|
||||
EmitIPCEvent(context, internal, channel, args, sender_id);
|
||||
|
||||
// Also send the message to all sub-frames.
|
||||
// TODO(MarshallOfSound): Completely move this logic to the main process
|
||||
|
@ -168,12 +172,38 @@ void ElectronApiServiceImpl::Message(bool internal,
|
|||
if (child->IsWebLocalFrame()) {
|
||||
v8::Local<v8::Context> child_context =
|
||||
renderer_client_->GetContext(child->ToWebLocalFrame(), isolate);
|
||||
EmitIPCEvent(child_context, internal, channel, arguments.GetList(),
|
||||
sender_id);
|
||||
EmitIPCEvent(child_context, internal, channel, args, sender_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
|
||||
void ElectronApiServiceImpl::DereferenceRemoteJSCallback(
|
||||
const std::string& context_id,
|
||||
int32_t object_id) {
|
||||
const auto* channel = "ELECTRON_RENDERER_RELEASE_CALLBACK";
|
||||
if (!document_created_)
|
||||
return;
|
||||
blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
|
||||
if (!frame)
|
||||
return;
|
||||
|
||||
v8::Isolate* isolate = blink::MainThreadIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
v8::Local<v8::Context> context = renderer_client_->GetContext(frame, isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
base::ListValue args;
|
||||
args.AppendString(context_id);
|
||||
args.AppendInteger(object_id);
|
||||
|
||||
v8::Local<v8::Value> v8_args = gin::ConvertToV8(isolate, args);
|
||||
EmitIPCEvent(context, true /* internal */, channel, v8_args,
|
||||
0 /* sender_id */);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ElectronApiServiceImpl::UpdateCrashpadPipeName(
|
||||
const std::string& pipe_name) {
|
||||
#if defined(OS_WIN)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "base/memory/weak_ptr.h"
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
#include "content/public/renderer/render_frame_observer.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "electron/shell/common/api/api.mojom.h"
|
||||
#include "mojo/public/cpp/bindings/associated_binding.h"
|
||||
|
||||
|
@ -28,8 +29,12 @@ class ElectronApiServiceImpl : public mojom::ElectronRenderer,
|
|||
void Message(bool internal,
|
||||
bool send_to_all,
|
||||
const std::string& channel,
|
||||
base::Value arguments,
|
||||
blink::CloneableMessage arguments,
|
||||
int32_t sender_id) override;
|
||||
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
|
||||
void DereferenceRemoteJSCallback(const std::string& context_id,
|
||||
int32_t object_id) override;
|
||||
#endif
|
||||
void UpdateCrashpadPipeName(const std::string& pipe_name) override;
|
||||
void TakeHeapSnapshot(mojo::ScopedHandle file,
|
||||
TakeHeapSnapshotCallback callback) override;
|
||||
|
|
|
@ -503,7 +503,7 @@ describe('app module', () => {
|
|||
await w.loadURL('about:blank')
|
||||
|
||||
const promise = emittedOnce(app, 'remote-get-current-window')
|
||||
w.webContents.executeJavaScript(`require('electron').remote.getCurrentWindow()`)
|
||||
w.webContents.executeJavaScript(`{ require('electron').remote.getCurrentWindow() }`)
|
||||
|
||||
const [, webContents] = await promise
|
||||
expect(webContents).to.equal(w.webContents)
|
||||
|
@ -519,7 +519,7 @@ describe('app module', () => {
|
|||
await w.loadURL('about:blank')
|
||||
|
||||
const promise = emittedOnce(app, 'remote-get-current-web-contents')
|
||||
w.webContents.executeJavaScript(`require('electron').remote.getCurrentWebContents()`)
|
||||
w.webContents.executeJavaScript(`{ require('electron').remote.getCurrentWebContents() }`)
|
||||
|
||||
const [, webContents] = await promise
|
||||
expect(webContents).to.equal(w.webContents)
|
||||
|
|
|
@ -1588,7 +1588,7 @@ describe('BrowserWindow module', () => {
|
|||
})
|
||||
w.loadFile(path.join(fixtures, 'api', 'preload.html'))
|
||||
const [, test] = await emittedOnce(ipcMain, 'answer')
|
||||
expect(test.toString()).to.eql('buffer')
|
||||
expect(test).to.eql(Buffer.from('buffer'))
|
||||
})
|
||||
it('has synchronous access to all eventual window APIs', async () => {
|
||||
const preload = path.join(fixtures, 'module', 'access-blink-apis.js')
|
||||
|
@ -1630,13 +1630,7 @@ describe('BrowserWindow module', () => {
|
|||
|
||||
const generateSpecs = (description: string, sandbox: boolean) => {
|
||||
describe(description, () => {
|
||||
it('loads the script before other scripts in window including normal preloads', function (done) {
|
||||
ipcMain.once('vars', function (event, preload1, preload2, preload3) {
|
||||
expect(preload1).to.equal('preload-1')
|
||||
expect(preload2).to.equal('preload-1-2')
|
||||
expect(preload3).to.be.null('preload 3')
|
||||
done()
|
||||
})
|
||||
it('loads the script before other scripts in window including normal preloads', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
|
@ -1645,6 +1639,10 @@ describe('BrowserWindow module', () => {
|
|||
}
|
||||
})
|
||||
w.loadURL('about:blank')
|
||||
const [, preload1, preload2, preload3] = await emittedOnce(ipcMain, 'vars')
|
||||
expect(preload1).to.equal('preload-1')
|
||||
expect(preload2).to.equal('preload-1-2')
|
||||
expect(preload3).to.be.undefined('preload 3')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ ifdescribe(features.isDesktopCapturerEnabled() && !process.arch.includes('arm')
|
|||
|
||||
const getSources: typeof desktopCapturer.getSources = (options: SourcesOptions) => {
|
||||
return w.webContents.executeJavaScript(`
|
||||
require('electron').desktopCapturer.getSources(${JSON.stringify(options)})
|
||||
require('electron').desktopCapturer.getSources(${JSON.stringify(options)}).then(m => JSON.parse(JSON.stringify(m)))
|
||||
`)
|
||||
}
|
||||
|
||||
|
|
|
@ -31,14 +31,14 @@ describe('ipcRenderer module', () => {
|
|||
expect(received).to.deep.equal(obj)
|
||||
})
|
||||
|
||||
it('can send instances of Date as ISO strings', async () => {
|
||||
it('can send instances of Date as Dates', async () => {
|
||||
const isoDate = new Date().toISOString()
|
||||
w.webContents.executeJavaScript(`{
|
||||
const { ipcRenderer } = require('electron')
|
||||
ipcRenderer.send('message', new Date(${JSON.stringify(isoDate)}))
|
||||
}`)
|
||||
const [, received] = await emittedOnce(ipcMain, 'message')
|
||||
expect(received).to.equal(isoDate)
|
||||
expect(received.toISOString()).to.equal(isoDate)
|
||||
})
|
||||
|
||||
it('can send instances of Buffer', async () => {
|
||||
|
@ -48,10 +48,12 @@ describe('ipcRenderer module', () => {
|
|||
ipcRenderer.send('message', Buffer.from(${JSON.stringify(data)}))
|
||||
}`)
|
||||
const [, received] = await emittedOnce(ipcMain, 'message')
|
||||
expect(received).to.be.an.instanceOf(Buffer)
|
||||
expect(received).to.be.an.instanceOf(Uint8Array)
|
||||
expect(Buffer.from(data).equals(received)).to.be.true()
|
||||
})
|
||||
|
||||
// TODO(nornagon): Change this test to expect an exception to be thrown in
|
||||
// Electron 9.
|
||||
it('can send objects with DOM class prototypes', async () => {
|
||||
w.webContents.executeJavaScript(`{
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
@ -62,21 +64,20 @@ describe('ipcRenderer module', () => {
|
|||
expect(value.hostname).to.equal('')
|
||||
})
|
||||
|
||||
it('does not crash on external objects (regression)', async () => {
|
||||
// TODO(nornagon): Change this test to expect an exception to be thrown in
|
||||
// Electron 9.
|
||||
it('does not crash when sending external objects', async () => {
|
||||
w.webContents.executeJavaScript(`{
|
||||
const { ipcRenderer } = require('electron')
|
||||
const http = require('http')
|
||||
|
||||
const request = http.request({ port: 5000, hostname: '127.0.0.1', method: 'GET', path: '/' })
|
||||
const stream = request.agent.sockets['127.0.0.1:5000:'][0]._handle._externalStream
|
||||
request.on('error', () => {})
|
||||
|
||||
ipcRenderer.send('message', request, stream)
|
||||
ipcRenderer.send('message', stream)
|
||||
}`)
|
||||
const [, requestValue, externalStreamValue] = await emittedOnce(ipcMain, 'message')
|
||||
const [, externalStreamValue] = await emittedOnce(ipcMain, 'message')
|
||||
|
||||
expect(requestValue.method).to.equal('GET')
|
||||
expect(requestValue.path).to.equal('/')
|
||||
expect(externalStreamValue).to.be.null()
|
||||
})
|
||||
|
||||
|
@ -104,7 +105,7 @@ describe('ipcRenderer module', () => {
|
|||
expect(childValue).to.deep.equal(child)
|
||||
})
|
||||
|
||||
it('inserts null for cyclic references', async () => {
|
||||
it('can handle cyclic references', async () => {
|
||||
w.webContents.executeJavaScript(`{
|
||||
const { ipcRenderer } = require('electron')
|
||||
const array = [5]
|
||||
|
@ -117,10 +118,10 @@ describe('ipcRenderer module', () => {
|
|||
|
||||
const [, arrayValue, childValue] = await emittedOnce(ipcMain, 'message')
|
||||
expect(arrayValue[0]).to.equal(5)
|
||||
expect(arrayValue[1]).to.be.null()
|
||||
expect(arrayValue[1]).to.equal(arrayValue)
|
||||
|
||||
expect(childValue.hello).to.equal('world')
|
||||
expect(childValue.child).to.be.null()
|
||||
expect(childValue.child).to.equal(childValue)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -182,9 +183,6 @@ describe('ipcRenderer module', () => {
|
|||
generateSpecs('with contextIsolation', { contextIsolation: true })
|
||||
generateSpecs('with contextIsolation + sandbox', { contextIsolation: true, sandbox: true })
|
||||
})
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
describe('ipcRenderer.on', () => {
|
||||
it('is not used for internals', async () => {
|
||||
|
|
|
@ -84,19 +84,14 @@ describe('renderer nodeIntegrationInSubFrames', () => {
|
|||
w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`))
|
||||
const details = await detailsPromise
|
||||
const senders = details.map(event => event[0].sender)
|
||||
await new Promise(async resolve => {
|
||||
let resultCount = 0
|
||||
senders.forEach(async sender => {
|
||||
const result = await sender.webContents.executeJavaScript('window.isolatedGlobal')
|
||||
if (webPreferences.contextIsolation) {
|
||||
expect(result).to.be.null()
|
||||
} else {
|
||||
expect(result).to.equal(true)
|
||||
}
|
||||
resultCount++
|
||||
if (resultCount === senders.length) resolve()
|
||||
})
|
||||
})
|
||||
const isolatedGlobals = await Promise.all(senders.map(sender => sender.webContents.executeJavaScript('window.isolatedGlobal')))
|
||||
for (const result of isolatedGlobals) {
|
||||
if (webPreferences.contextIsolation) {
|
||||
expect(result).to.be.undefined()
|
||||
} else {
|
||||
expect(result).to.equal(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -199,6 +199,7 @@ describe('webContents module', () => {
|
|||
var iframe = document.createElement('iframe')
|
||||
iframe.src = '${serverUrl}/slow'
|
||||
document.body.appendChild(iframe)
|
||||
null // don't return the iframe
|
||||
`).then(() => {
|
||||
w.webContents.executeJavaScript('console.log(\'hello\')').then(() => {
|
||||
done()
|
||||
|
@ -215,15 +216,6 @@ describe('webContents module', () => {
|
|||
})
|
||||
w.loadURL(serverUrl)
|
||||
})
|
||||
|
||||
it('works with result objects that have DOM class prototypes', (done) => {
|
||||
w.webContents.executeJavaScript('document.location').then(result => {
|
||||
expect(result.origin).to.equal(serverUrl)
|
||||
expect(result.protocol).to.equal('http:')
|
||||
done()
|
||||
})
|
||||
w.loadURL(serverUrl)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ describe('node feature', () => {
|
|||
}
|
||||
function errorDataListener (data: Buffer) {
|
||||
output += data
|
||||
if (output.trim().startsWith('Debugger listening on ws://')) {
|
||||
if (/^Debugger listening on ws:/m.test(output)) {
|
||||
cleanup()
|
||||
done()
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ describe('node feature', () => {
|
|||
}
|
||||
function errorDataListener (data: Buffer) {
|
||||
output += data
|
||||
if (output.trim().startsWith('Debugger listening on ws://')) {
|
||||
if (/^Debugger listening on ws:/m.test(output)) {
|
||||
expect(output.trim()).to.contain(':17364', 'should be listening on port 17364')
|
||||
cleanup()
|
||||
done()
|
||||
|
@ -203,4 +203,4 @@ describe('node feature', () => {
|
|||
const result = childProcess.spawnSync(process.execPath, [path.resolve(fixtures, 'api', 'electron-main-module', 'app.asar')])
|
||||
expect(result.status).to.equal(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -545,4 +545,4 @@ describe('<webview> tag', function () {
|
|||
})
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
|
|
@ -241,14 +241,14 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => {
|
|||
const print = path.join(fixtures, 'module', 'print_name.js')
|
||||
const printName = remote.require(print)
|
||||
|
||||
it('converts NaN to undefined', () => {
|
||||
expect(printName.getNaN()).to.be.undefined()
|
||||
expect(printName.echo(NaN)).to.be.undefined()
|
||||
it('preserves NaN', () => {
|
||||
expect(printName.getNaN()).to.be.NaN()
|
||||
expect(printName.echo(NaN)).to.be.NaN()
|
||||
})
|
||||
|
||||
it('converts Infinity to undefined', () => {
|
||||
expect(printName.getInfinity()).to.be.undefined()
|
||||
expect(printName.echo(Infinity)).to.be.undefined()
|
||||
it('preserves Infinity', () => {
|
||||
expect(printName.getInfinity()).to.equal(Infinity)
|
||||
expect(printName.echo(Infinity)).to.equal(Infinity)
|
||||
})
|
||||
|
||||
it('keeps its constructor name for objects', () => {
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
creationTime: invoke(() => process.getCreationTime()),
|
||||
heapStatistics: invoke(() => process.getHeapStatistics()),
|
||||
blinkMemoryInfo: invoke(() => process.getBlinkMemoryInfo()),
|
||||
processMemoryInfo: invoke(() => process.getProcessMemoryInfo()),
|
||||
processMemoryInfo: invoke(() => process.getProcessMemoryInfo() ? {} : null),
|
||||
systemMemoryInfo: invoke(() => process.getSystemMemoryInfo()),
|
||||
systemVersion: invoke(() => process.getSystemVersion()),
|
||||
cpuUsage: invoke(() => process.getCPUUsage()),
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
SendZoomLevel().then(() => {
|
||||
if (!finalNavigation) {
|
||||
finalNavigation = true
|
||||
view.executeJavaScript('window.location.hash=123', () => {})
|
||||
view.executeJavaScript('window.location.hash=123')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1068,7 +1068,7 @@ describe('<webview> tag', function () {
|
|||
await loadWebView(webview, { src })
|
||||
|
||||
const data = await webview.printToPDF({})
|
||||
expect(data).to.be.an.instanceof(Buffer).that.is.not.empty()
|
||||
expect(data).to.be.an.instanceof(Uint8Array).that.is.not.empty()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче