Skip to content

Commit 268cd39

Browse files
feat: add a new contextBridge module (electron#20639)
* feat: add a new contextBridge module (electron#20307) * feat: add a new contextBridge module * chore: fix docs linting * feat: add support for function arguments being proxied * chore: ensure that contextBridge can only be used when contextIsolation is enabled * docs: getReverseBinding can be null * docs: fix broken links in md file * feat: add support for promises in function parameters * fix: linting failure for explicit constructor * Update atom_api_context_bridge.cc * chore: update docs and API design as per feedback * refactor: remove reverse bindings and handle GC'able functions across the bridge * chore: only expose debugGC in testing builds * fix: do not proxy promises as objects * spec: add complete spec coverage for contextBridge * spec: add tests for null/undefined and the anti-overwrite logic * chore: fix linting * spec: add complex nested back-and-forth function calling * fix: expose contextBridge in sandboxed renderers * refactor: improve security of default_app using the new contextBridge module * s/bindAPIInMainWorld/exposeInMainWorld * chore: sorry for this commit, its a big one, I fixed like everything and refactored a lot * chore: remove PassedValueCache as it is unused now Values transferred from context A to context B are now cachde in the RenderFramePersistenceStore * chore: move to anonymous namespace * refactor: remove PassValueToOtherContextWithCache * chore: remove commented unused code blocks * chore: remove .only * chore: remote commented code * refactor: extract RenderFramePersistenceStore * spec: ensure it works with numbered keys * fix: handle number keys correctly * fix: sort out the linter * spec: update default_app asar spec for removed file * refactor: change signatures to return v8 objects directly rather than the mate dictionary handle * refactor: use the v8 serializer to support cloneable buffers and other object types * chore: fix linting * fix: handle hash collisions with a linked list in the map * fix: enforce a recursion limit on the context bridge * chore: fix linting * chore: remove TODO * chore: adapt for PR feedback * chore: remove .only * chore: clean up docs and clean up the proxy map when objects are released * chore: ensure we cache object values that are cloned through the V8 serializer * docs: mark contextBridge as experimental (electron#20638) * docs: mark contextBridge as experimental This commit didn't make it to the original PR, quick addition here * Update context-bridge.md * chore: touch up the differences between master and 6-0-x * chore: add v8 serializer converter, cherry picked from 2fad53e * chore: support converting OnceCallback to V8 (electron#17941) * chore: fixup tests * chore: fix linting * chore: add patch for mojo message constructor
1 parent 3ca62d9 commit 268cd39

29 files changed

+2220
-59
lines changed

atom/browser/api/atom_api_web_contents.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@
3535
#include "atom/common/color_util.h"
3636
#include "atom/common/mouse_util.h"
3737
#include "atom/common/native_mate_converters/blink_converter.h"
38-
#include "atom/common/native_mate_converters/callback.h"
3938
#include "atom/common/native_mate_converters/content_converter.h"
4039
#include "atom/common/native_mate_converters/file_path_converter.h"
4140
#include "atom/common/native_mate_converters/gfx_converter.h"
4241
#include "atom/common/native_mate_converters/gurl_converter.h"
4342
#include "atom/common/native_mate_converters/image_converter.h"
4443
#include "atom/common/native_mate_converters/net_converter.h"
4544
#include "atom/common/native_mate_converters/network_converter.h"
45+
#include "atom/common/native_mate_converters/once_callback.h"
4646
#include "atom/common/native_mate_converters/string16_converter.h"
4747
#include "atom/common/native_mate_converters/value_converter.h"
4848
#include "atom/common/node_includes.h"

atom/common/native_mate_converters/blink_converter.cc

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66

77
#include <algorithm>
88
#include <string>
9+
#include <utility>
910
#include <vector>
1011

1112
#include "atom/common/keyboard_util.h"
13+
#include "atom/common/native_mate_converters/value_converter.h"
1214
#include "base/strings/string_util.h"
1315
#include "base/strings/utf_string_conversions.h"
1416
#include "content/public/browser/native_web_keyboard_event.h"
1517
#include "gin/converter.h"
18+
#include "mojo/public/cpp/base/values_mojom_traits.h"
19+
#include "mojo/public/mojom/base/values.mojom.h"
1620
#include "native_mate/dictionary.h"
1721
#include "third_party/blink/public/platform/web_input_event.h"
1822
#include "third_party/blink/public/platform/web_mouse_event.h"
@@ -527,4 +531,175 @@ bool Converter<network::mojom::ReferrerPolicy>::FromV8(
527531
return true;
528532
}
529533

534+
namespace {
535+
constexpr uint8_t kNewSerializationTag = 0;
536+
constexpr uint8_t kOldSerializationTag = 1;
537+
538+
class V8Serializer : public v8::ValueSerializer::Delegate {
539+
public:
540+
explicit V8Serializer(v8::Isolate* isolate,
541+
bool use_old_serialization = false)
542+
: isolate_(isolate),
543+
serializer_(isolate, this),
544+
use_old_serialization_(use_old_serialization) {}
545+
~V8Serializer() override = default;
546+
547+
bool Serialize(v8::Local<v8::Value> value, blink::CloneableMessage* out) {
548+
serializer_.WriteHeader();
549+
if (use_old_serialization_) {
550+
WriteTag(kOldSerializationTag);
551+
if (!WriteBaseValue(value)) {
552+
isolate_->ThrowException(
553+
mate::StringToV8(isolate_, "An object could not be cloned."));
554+
return false;
555+
}
556+
} else {
557+
WriteTag(kNewSerializationTag);
558+
bool wrote_value;
559+
v8::TryCatch try_catch(isolate_);
560+
if (!serializer_.WriteValue(isolate_->GetCurrentContext(), value)
561+
.To(&wrote_value)) {
562+
try_catch.Reset();
563+
if (!V8Serializer(isolate_, true).Serialize(value, out)) {
564+
try_catch.ReThrow();
565+
return false;
566+
}
567+
return true;
568+
}
569+
DCHECK(wrote_value);
570+
}
571+
572+
std::pair<uint8_t*, size_t> buffer = serializer_.Release();
573+
DCHECK_EQ(buffer.first, data_.data());
574+
out->encoded_message = base::make_span(buffer.first, buffer.second);
575+
out->owned_encoded_message = std::move(data_);
576+
577+
return true;
578+
}
579+
580+
bool WriteBaseValue(v8::Local<v8::Value> object) {
581+
base::Value value;
582+
if (!ConvertFromV8(isolate_, object, &value)) {
583+
return false;
584+
}
585+
mojo::Message message = mojo_base::mojom::Value::SerializeAsMessage(&value);
586+
587+
serializer_.WriteUint32(message.data_num_bytes());
588+
serializer_.WriteRawBytes(message.data(), message.data_num_bytes());
589+
return true;
590+
}
591+
592+
void WriteTag(uint8_t tag) { serializer_.WriteRawBytes(&tag, 1); }
593+
594+
// v8::ValueSerializer::Delegate
595+
void* ReallocateBufferMemory(void* old_buffer,
596+
size_t size,
597+
size_t* actual_size) override {
598+
DCHECK_EQ(old_buffer, data_.data());
599+
data_.resize(size);
600+
*actual_size = data_.capacity();
601+
return data_.data();
602+
}
603+
604+
void FreeBufferMemory(void* buffer) override {
605+
DCHECK_EQ(buffer, data_.data());
606+
data_ = {};
607+
}
608+
609+
void ThrowDataCloneError(v8::Local<v8::String> message) override {
610+
isolate_->ThrowException(v8::Exception::Error(message));
611+
}
612+
613+
private:
614+
v8::Isolate* isolate_;
615+
std::vector<uint8_t> data_;
616+
v8::ValueSerializer serializer_;
617+
bool use_old_serialization_;
618+
};
619+
620+
class V8Deserializer : public v8::ValueDeserializer::Delegate {
621+
public:
622+
V8Deserializer(v8::Isolate* isolate, const blink::CloneableMessage& message)
623+
: isolate_(isolate),
624+
deserializer_(isolate,
625+
message.encoded_message.data(),
626+
message.encoded_message.size(),
627+
this) {}
628+
629+
v8::Local<v8::Value> Deserialize() {
630+
v8::EscapableHandleScope scope(isolate_);
631+
auto context = isolate_->GetCurrentContext();
632+
bool read_header;
633+
if (!deserializer_.ReadHeader(context).To(&read_header))
634+
return v8::Null(isolate_);
635+
DCHECK(read_header);
636+
uint8_t tag;
637+
if (!ReadTag(&tag))
638+
return v8::Null(isolate_);
639+
switch (tag) {
640+
case kNewSerializationTag: {
641+
v8::Local<v8::Value> value;
642+
if (!deserializer_.ReadValue(context).ToLocal(&value)) {
643+
return v8::Null(isolate_);
644+
}
645+
return scope.Escape(value);
646+
}
647+
case kOldSerializationTag: {
648+
v8::Local<v8::Value> value;
649+
if (!ReadBaseValue(&value)) {
650+
return v8::Null(isolate_);
651+
}
652+
return scope.Escape(value);
653+
}
654+
default:
655+
NOTREACHED() << "Invalid tag: " << tag;
656+
return v8::Null(isolate_);
657+
}
658+
}
659+
660+
bool ReadTag(uint8_t* tag) {
661+
const void* tag_bytes;
662+
if (!deserializer_.ReadRawBytes(1, &tag_bytes))
663+
return false;
664+
*tag = *reinterpret_cast<const uint8_t*>(tag_bytes);
665+
return true;
666+
}
667+
668+
bool ReadBaseValue(v8::Local<v8::Value>* value) {
669+
uint32_t length;
670+
const void* data;
671+
if (!deserializer_.ReadUint32(&length) ||
672+
!deserializer_.ReadRawBytes(length, &data)) {
673+
return false;
674+
}
675+
mojo::Message message(
676+
base::make_span(reinterpret_cast<const uint8_t*>(data), length), {});
677+
base::Value out;
678+
if (!mojo_base::mojom::Value::DeserializeFromMessage(std::move(message),
679+
&out)) {
680+
return false;
681+
}
682+
*value = ConvertToV8(isolate_, out);
683+
return true;
684+
}
685+
686+
private:
687+
v8::Isolate* isolate_;
688+
v8::ValueDeserializer deserializer_;
689+
};
690+
691+
} // namespace
692+
693+
v8::Local<v8::Value> Converter<blink::CloneableMessage>::ToV8(
694+
v8::Isolate* isolate,
695+
const blink::CloneableMessage& in) {
696+
return V8Deserializer(isolate, in).Deserialize();
697+
}
698+
699+
bool Converter<blink::CloneableMessage>::FromV8(v8::Isolate* isolate,
700+
v8::Handle<v8::Value> val,
701+
blink::CloneableMessage* out) {
702+
return V8Serializer(isolate).Serialize(val, out);
703+
}
704+
530705
} // namespace mate

atom/common/native_mate_converters/blink_converter.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_BLINK_CONVERTER_H_
77

88
#include "native_mate/converter.h"
9+
#include "third_party/blink/public/common/messaging/cloneable_message.h"
910
#include "third_party/blink/public/platform/web_cache.h"
1011
#include "third_party/blink/public/platform/web_input_event.h"
1112
#include "third_party/blink/public/web/web_context_menu_data.h"
@@ -131,6 +132,15 @@ struct Converter<network::mojom::ReferrerPolicy> {
131132
network::mojom::ReferrerPolicy* out);
132133
};
133134

135+
template <>
136+
struct Converter<blink::CloneableMessage> {
137+
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
138+
const blink::CloneableMessage& in);
139+
static bool FromV8(v8::Isolate* isolate,
140+
v8::Local<v8::Value> val,
141+
blink::CloneableMessage* out);
142+
};
143+
134144
v8::Local<v8::Value> EditFlagsToV8(v8::Isolate* isolate, int editFlags);
135145
v8::Local<v8::Value> MediaFlagsToV8(v8::Isolate* isolate, int mediaFlags);
136146

atom/common/native_mate_converters/callback.h

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_CALLBACK_H_
66
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_CALLBACK_H_
77

8+
#include <utility>
89
#include <vector>
910

1011
#include "atom/common/api/locker.h"
@@ -54,7 +55,8 @@ struct V8FunctionInvoker<v8::Local<v8::Value>(ArgTypes...)> {
5455
v8::Local<v8::Function> holder = function.NewHandle(isolate);
5556
v8::Local<v8::Context> context = holder->CreationContext();
5657
v8::Context::Scope context_scope(context);
57-
std::vector<v8::Local<v8::Value>> args{ConvertToV8(isolate, raw)...};
58+
std::vector<v8::Local<v8::Value>> args{
59+
ConvertToV8(isolate, std::forward<ArgTypes>(raw))...};
5860
v8::MaybeLocal<v8::Value> ret = holder->Call(
5961
context, holder, args.size(), args.empty() ? nullptr : &args.front());
6062
if (ret.IsEmpty())
@@ -78,7 +80,8 @@ struct V8FunctionInvoker<void(ArgTypes...)> {
7880
v8::Local<v8::Function> holder = function.NewHandle(isolate);
7981
v8::Local<v8::Context> context = holder->CreationContext();
8082
v8::Context::Scope context_scope(context);
81-
std::vector<v8::Local<v8::Value>> args{ConvertToV8(isolate, raw)...};
83+
std::vector<v8::Local<v8::Value>> args{
84+
ConvertToV8(isolate, std::forward<ArgTypes>(raw))...};
8285
holder
8386
->Call(context, holder, args.size(),
8487
args.empty() ? nullptr : &args.front())
@@ -101,7 +104,8 @@ struct V8FunctionInvoker<ReturnType(ArgTypes...)> {
101104
v8::Local<v8::Function> holder = function.NewHandle(isolate);
102105
v8::Local<v8::Context> context = holder->CreationContext();
103106
v8::Context::Scope context_scope(context);
104-
std::vector<v8::Local<v8::Value>> args{ConvertToV8(isolate, raw)...};
107+
std::vector<v8::Local<v8::Value>> args{
108+
ConvertToV8(isolate, std::forward<ArgTypes>(raw))...};
105109
v8::Local<v8::Value> result;
106110
auto maybe_result = holder->Call(context, holder, args.size(),
107111
args.empty() ? nullptr : &args.front());
@@ -138,20 +142,6 @@ struct NativeFunctionInvoker<ReturnType(ArgTypes...)> {
138142

139143
} // namespace internal
140144

141-
template <typename Sig>
142-
struct Converter<base::OnceCallback<Sig>> {
143-
static bool FromV8(v8::Isolate* isolate,
144-
v8::Local<v8::Value> val,
145-
base::OnceCallback<Sig>* out) {
146-
if (!val->IsFunction())
147-
return false;
148-
149-
*out = base::BindOnce(&internal::V8FunctionInvoker<Sig>::Go, isolate,
150-
internal::SafeV8Function(isolate, val));
151-
return true;
152-
}
153-
};
154-
155145
template <typename Sig>
156146
struct Converter<base::RepeatingCallback<Sig>> {
157147
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) 2019 GitHub, Inc. All rights reserved.
2+
// Use of this source code is governed by the MIT license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_ONCE_CALLBACK_H_
6+
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_ONCE_CALLBACK_H_
7+
8+
#include <utility>
9+
10+
#include "atom/common/native_mate_converters/callback.h"
11+
12+
namespace mate {
13+
14+
namespace internal {
15+
16+
// Manages the OnceCallback with ref-couting.
17+
template <typename Sig>
18+
class RefCountedOnceCallback
19+
: public base::RefCounted<RefCountedOnceCallback<Sig>> {
20+
public:
21+
explicit RefCountedOnceCallback(base::OnceCallback<Sig> callback)
22+
: callback_(std::move(callback)) {}
23+
24+
base::OnceCallback<Sig> GetCallback() { return std::move(callback_); }
25+
26+
private:
27+
friend class base::RefCounted<RefCountedOnceCallback<Sig>>;
28+
~RefCountedOnceCallback() = default;
29+
30+
base::OnceCallback<Sig> callback_;
31+
};
32+
33+
// Invokes the OnceCallback.
34+
template <typename Sig>
35+
struct InvokeOnceCallback {};
36+
37+
template <typename... ArgTypes>
38+
struct InvokeOnceCallback<void(ArgTypes...)> {
39+
static void Go(
40+
scoped_refptr<RefCountedOnceCallback<void(ArgTypes...)>> holder,
41+
ArgTypes... args) {
42+
base::OnceCallback<void(ArgTypes...)> callback = holder->GetCallback();
43+
DCHECK(!callback.is_null());
44+
std::move(callback).Run(std::move(args)...);
45+
}
46+
};
47+
48+
template <typename ReturnType, typename... ArgTypes>
49+
struct InvokeOnceCallback<ReturnType(ArgTypes...)> {
50+
static ReturnType Go(
51+
scoped_refptr<RefCountedOnceCallback<ReturnType(ArgTypes...)>> holder,
52+
ArgTypes... args) {
53+
base::OnceCallback<void(ArgTypes...)> callback = holder->GetCallback();
54+
DCHECK(!callback.is_null());
55+
return std::move(callback).Run(std::move(args)...);
56+
}
57+
};
58+
59+
} // namespace internal
60+
61+
template <typename Sig>
62+
struct Converter<base::OnceCallback<Sig>> {
63+
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
64+
base::OnceCallback<Sig> val) {
65+
// Reuse the converter of base::RepeatingCallback by storing the callback
66+
// with a RefCounted.
67+
auto holder = base::MakeRefCounted<internal::RefCountedOnceCallback<Sig>>(
68+
std::move(val));
69+
return Converter<base::RepeatingCallback<Sig>>::ToV8(
70+
isolate,
71+
base::BindRepeating(&internal::InvokeOnceCallback<Sig>::Go, holder));
72+
}
73+
74+
static bool FromV8(v8::Isolate* isolate,
75+
v8::Local<v8::Value> val,
76+
base::OnceCallback<Sig>* out) {
77+
if (!val->IsFunction())
78+
return false;
79+
*out = base::BindOnce(&internal::V8FunctionInvoker<Sig>::Go, isolate,
80+
internal::SafeV8Function(isolate, val));
81+
return true;
82+
}
83+
};
84+
85+
} // namespace mate
86+
87+
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_ONCE_CALLBACK_H_

atom/common/node_bindings.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
V(atom_common_screen) \
6666
V(atom_common_shell) \
6767
V(atom_common_v8_util) \
68+
V(atom_renderer_context_bridge) \
6869
V(atom_renderer_ipc) \
6970
V(atom_renderer_web_frame)
7071

0 commit comments

Comments
 (0)