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:
Jeremy Apthorp 2019-10-09 10:59:08 -07:00 коммит произвёл John Kleinschmidt
Родитель c250cd6e7c
Коммит 2fad53e66b
38 изменённых файлов: 623 добавлений и 169 удалений

Просмотреть файл

@ -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', () => {

2
spec/fixtures/module/preload-sandbox.js поставляемый
Просмотреть файл

@ -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()
})
})