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:
parent
c250cd6e7c
commit
2fad53e66b
38 changed files with 623 additions and 169 deletions
|
@ -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
|
||||
|
|
30
shell/common/gin_converters/blink_converter_gin_adapter.h
Normal file
30
shell/common/gin_converters/blink_converter_gin_adapter.h
Normal file
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue