Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

refactor: use v8 serialization for ipc #20214

Merged
merged 56 commits into from Oct 9, 2019
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
0f092dc
refactor: use v8 serialization for ipc
nornagon Sep 12, 2019
f8e14c4
cloning process.env doesn't work
nornagon Sep 12, 2019
3ab77d2
serialize host objects by enumerating key/values
nornagon Sep 16, 2019
94e085b
new serialization can handle NaN, Infinity, and undefined correctly
nornagon Sep 16, 2019
a43bbbc
can't allocate v8 objects during GC
nornagon Sep 16, 2019
8301091
backport microtasks fix
nornagon Sep 16, 2019
245ee49
Merge remote-tracking branch 'origin/master' into cloneable-message-ipc
nornagon Sep 19, 2019
df429a0
fix compile
nornagon Sep 20, 2019
2738f42
fix node_stream_loader reentrancy
nornagon Sep 20, 2019
3284dcd
update subframe spec to expect undefined instead of null
nornagon Sep 20, 2019
2cbf4ca
write undefined instead of crashing when serializing host objects
nornagon Sep 20, 2019
0a52bb8
fix webview spec
nornagon Sep 20, 2019
5e0565a
fix download spec
nornagon Sep 20, 2019
40e1fad
buffers are transformed into uint8arrays
nornagon Sep 20, 2019
955c796
can't serialize promises
nornagon Sep 20, 2019
58f0cd9
fix chrome.i18n.getMessage
nornagon Sep 20, 2019
5940a3b
fix devtools tests
nornagon Sep 20, 2019
17d18f1
fix zoom test
nornagon Sep 20, 2019
bc9a3f1
fix debug build
nornagon Sep 20, 2019
08f8331
fix lint
nornagon Sep 20, 2019
02d27a7
update ipcRenderer tests
nornagon Sep 20, 2019
df7db15
Merge remote-tracking branch 'origin/master' into cloneable-message-ipc
nornagon Sep 20, 2019
bf44165
fix printToPDF test
nornagon Sep 20, 2019
b3baeae
Merge remote-tracking branch 'origin/master' into cloneable-message-ipc
nornagon Sep 23, 2019
64702c0
update patch
nornagon Sep 23, 2019
712c652
remove accidentally re-added remote-side spec
nornagon Sep 23, 2019
674bffd
wip
nornagon Sep 23, 2019
c90e09f
don't attempt to serialize host objects
nornagon Sep 23, 2019
b89750e
jump through different hoops to set options.webContents sometimes
nornagon Sep 24, 2019
2faef13
whoops
nornagon Sep 24, 2019
4404b94
fix lint
nornagon Sep 24, 2019
853e895
clean up error-handling logic
nornagon Sep 24, 2019
6162988
fix memory leak
nornagon Sep 24, 2019
9d38a1e
fix lint
nornagon Sep 24, 2019
271c41f
convert host objects using old base::Value serialization
nornagon Sep 25, 2019
5383d56
fix lint more
nornagon Sep 25, 2019
2c53cc1
fall back to base::Value-based serialization
nornagon Sep 25, 2019
6e2e6c1
remove commented-out code
nornagon Sep 25, 2019
6270ca2
add docs to breaking-changes.md
nornagon Sep 30, 2019
d3a9e1c
Update breaking-changes.md
nornagon Sep 30, 2019
bcc470a
update ipcRenderer and WebContents docs
nornagon Oct 1, 2019
a970c4d
lint
nornagon Oct 1, 2019
153c3f5
Merge remote-tracking branch 'origin/master' into cloneable-message-ipc
nornagon Oct 1, 2019
96798b4
use named values for format tag
nornagon Sep 28, 2019
7f4fbe0
save a memcpy for ~30% speedup
nornagon Sep 28, 2019
e2f7ad4
get rid of calls to ShallowClone
nornagon Sep 28, 2019
f30b3a5
extra debugging for paranoia
nornagon Sep 28, 2019
27d9963
d'oh, use the correct named tags
nornagon Oct 1, 2019
215f786
Merge remote-tracking branch 'origin/master' into cloneable-message-ipc
nornagon Oct 2, 2019
aae6c48
apparently msstl doesn't like this DCHECK
nornagon Oct 2, 2019
f033c97
funny story about that DCHECK
nornagon Oct 3, 2019
4ac4714
disable remote-related functions when enable_remote_module = false
nornagon Oct 4, 2019
a3403a5
nits
nornagon Oct 4, 2019
2d6b332
use EnableIf to disable remote methods in mojom
nornagon Oct 4, 2019
fc668b4
fix include
nornagon Oct 4, 2019
55076a3
review comments
nornagon Oct 8, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 53 additions & 0 deletions docs/api/breaking-changes.md
Expand Up @@ -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)
jkleinsc marked this conversation as resolved.
Show resolved Hide resolved

### 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,
nornagon marked this conversation as resolved.
Show resolved Hide resolved
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(someUint8Array.buffer)
nornagon marked this conversation as resolved.
Show resolved Hide resolved
```

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.
nornagon marked this conversation as resolved.
Show resolved Hide resolved

[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
Expand Down
1 change: 1 addition & 0 deletions filenames.gni
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion lib/browser/chrome-extension.js
Expand Up @@ -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).then(b => b.toString('utf8'))
nornagon marked this conversation as resolved.
Show resolved Hide resolved
nornagon marked this conversation as resolved.
Show resolved Hide resolved
})

const validStorageTypes = new Set(['sync', 'local'])
Expand Down
14 changes: 13 additions & 1 deletion lib/browser/guest-view-manager.js
Expand Up @@ -23,7 +23,6 @@ const supportedWebViewEvents = [
'devtools-opened',
'devtools-closed',
'devtools-focused',
'new-window',
'will-navigate',
'did-start-navigation',
'did-navigate',
Expand All @@ -48,6 +47,13 @@ const supportedWebViewEvents = [
const guestInstances = {}
const embedderElementsMap = {}

function sanitizeOptionsForGuest (options) {
const ret = { ...options }
nornagon marked this conversation as resolved.
Show resolved Hide resolved
// 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) {
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 1 addition & 2 deletions lib/browser/guest-window-manager.js
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/browser/rpc-server.js
Expand Up @@ -114,7 +114,7 @@ ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event)
process: {
arch: process.arch,
platform: process.platform,
env: process.env,
env: { ...process.env },
nornagon marked this conversation as resolved.
Show resolved Hide resolved
version: process.version,
versions: process.versions,
execPath: process.helperExecPath
Expand Down
1 change: 1 addition & 0 deletions patches/chromium/.patches
Expand Up @@ -79,4 +79,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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a CL for this ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet, but I talked to pfeldman@ and he said he'd approve it if i uploaded it :P will do soon.


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) {
4 changes: 2 additions & 2 deletions patches/chromium/gin_with_namespace.patch
Expand Up @@ -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 {
Expand Down Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions patches/node/.patches
Expand Up @@ -39,3 +39,4 @@ chore_split_createenvironment_into_createenvironment_and.patch
chore_handle_default_configuration_not_being_set_in_the_electron_env.patch
revert_crypto_add_outputlength_option_to_crypto_createhash.patch
add_openssl_is_boringssl_guard_to_oaep_hash_check.patch
fix_microtasks.patch
58 changes: 58 additions & 0 deletions patches/node/fix_microtasks.patch
@@ -0,0 +1,58 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Mon, 16 Sep 2019 17:50:28 -0400
Subject: fix microtasks

backports https://github.com/nodejs/node/pull/29581 and https://github.com/nodejs/node/pull/29434

diff --git a/src/api/callback.cc b/src/api/callback.cc
index 43ccfafd9f2c85e23a9ea6277e88e4864e287905..3c518870c9c8d92f3dfcd6c270f5e023e3b69633 100644
--- a/src/api/callback.cc
+++ b/src/api/callback.cc
@@ -12,6 +12,7 @@ using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::MaybeLocal;
+using v8::MicrotasksScope;
using v8::NewStringType;
using v8::Object;
using v8::String;
@@ -100,7 +101,7 @@ void InternalCallbackScope::Close() {

if (!env_->can_call_into_js()) return;
if (!tick_info->has_tick_scheduled()) {
- env_->isolate()->RunMicrotasks();
+ MicrotasksScope::PerformCheckpoint(env_->isolate());
}

#if 0 // FIXME(codebytere): figure out why this check fails/causes crash
diff --git a/src/node_task_queue.cc b/src/node_task_queue.cc
index e6b4d0b8e211cdb1fef4759457c2550e28448360..918796ba77d80cf66324164a930f8068e0622ccb 100644
--- a/src/node_task_queue.cc
+++ b/src/node_task_queue.cc
@@ -21,6 +21,7 @@ using v8::kPromiseRejectWithNoHandler;
using v8::kPromiseResolveAfterResolved;
using v8::Local;
using v8::Message;
+using v8::MicrotasksScope;
using v8::Number;
using v8::Object;
using v8::Promise;
@@ -43,7 +44,7 @@ static void EnqueueMicrotask(const FunctionCallbackInfo<Value>& args) {
bool RunNextTicksNative(Environment* env) {
TickInfo* tick_info = env->tick_info();
if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn())
- env->isolate()->RunMicrotasks();
+ MicrotasksScope::PerformCheckpoint(env->isolate());
if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn())
return true;

@@ -54,7 +55,7 @@ bool RunNextTicksNative(Environment* env) {
}

static void RunMicrotasks(const FunctionCallbackInfo<Value>& args) {
- args.GetIsolate()->RunMicrotasks();
+ MicrotasksScope::PerformCheckpoint(args.GetIsolate());
}

static void SetTickCallback(const FunctionCallbackInfo<Value>& args) {
50 changes: 37 additions & 13 deletions shell/browser/api/atom_api_web_contents.cc
Expand Up @@ -1000,7 +1000,7 @@ void WebContents::OnElectronBrowserConnectionError() {

void WebContents::Message(bool internal,
const std::string& channel,
base::Value arguments) {
blink::CloneableMessage arguments) {
nornagon marked this conversation as resolved.
Show resolved Hide resolved
// webContents.emit('-ipc-message', new Event(), internal, channel,
// arguments);
EmitWithSender("-ipc-message", bindings_.dispatch_context(), base::nullopt,
Expand All @@ -1009,7 +1009,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(),
Expand All @@ -1018,7 +1018,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);
Expand All @@ -1030,24 +1030,35 @@ 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());
arguments.ShallowClone(), 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));
}

void WebContents::DereferenceRemoteJSObject(const std::string& context_id,
nornagon marked this conversation as resolved.
Show resolved Hide resolved
int object_id,
int ref_count) {
base::ListValue args;
args.AppendString(context_id);
args.AppendInteger(object_id);
args.AppendInteger(ref_count);
nornagon marked this conversation as resolved.
Show resolved Hide resolved
EmitWithSender("-ipc-message", bindings_.dispatch_context(), base::nullopt,
/* internal */ true, "ELECTRON_BROWSER_DEREFERENCE",
std::move(args));
}

void WebContents::UpdateDraggableRegions(
std::vector<mojom::DraggableRegionPtr> regions) {
for (ExtendedWebContentsObserver& observer : observers_)
Expand Down Expand Up @@ -1919,14 +1930,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) {
Expand All @@ -1942,7 +1960,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;
}
Expand All @@ -1951,7 +1969,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;
Expand All @@ -1964,7 +1988,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;
}
Expand Down