[extensions] Replace SafeBuiltins v8::Extension with Pure C++ V8 API [chromium/src : main]

0 views
Skip to first unread message

Jochen Eisinger (Gerrit)

unread,
Jul 8, 2026, 12:04:48 PM (yesterday) Jul 8
to Kentaro Hara, Camillo Bruni, Devlin Cronin, Raphael Kubo da Costa, Chromium LUCI CQ, android-bu...@system.gserviceaccount.com, chromium...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, hiroshig...@chromium.org, kouhei...@chromium.org, dom+...@chromium.org, chromium-a...@chromium.org, extension...@chromium.org
Attention needed from Camillo Bruni, Devlin Cronin and Kentaro Hara

Jochen Eisinger voted and added 1 comment

Votes added by Jochen Eisinger

Commit-Queue+1

1 comment

Patchset-level comments
File-level comment, Patchset 5 (Latest):
Jochen Eisinger . resolved

ptal

Open in Gerrit

Related details

Attention is currently required from:
  • Camillo Bruni
  • Devlin Cronin
  • Kentaro Hara
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: I90dc32553e5c12d1804dce4592c72642baaf92e2
Gerrit-Change-Number: 8063487
Gerrit-PatchSet: 5
Gerrit-Owner: Jochen Eisinger <joc...@chromium.org>
Gerrit-Reviewer: Camillo Bruni <cbr...@chromium.org>
Gerrit-Reviewer: Devlin Cronin <rdevlin...@chromium.org>
Gerrit-Reviewer: Jochen Eisinger <joc...@chromium.org>
Gerrit-Reviewer: Kentaro Hara <har...@chromium.org>
Gerrit-CC: Raphael Kubo da Costa <ku...@igalia.com>
Gerrit-Attention: Devlin Cronin <rdevlin...@chromium.org>
Gerrit-Attention: Kentaro Hara <har...@chromium.org>
Gerrit-Attention: Camillo Bruni <cbr...@chromium.org>
Gerrit-Comment-Date: Wed, 08 Jul 2026 16:04:32 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
satisfied_requirement
unsatisfied_requirement
open
diffy

Devlin Cronin (Gerrit)

unread,
2:49 PM (3 hours ago) 2:49 PM
to Jochen Eisinger, Kentaro Hara, Camillo Bruni, Devlin Cronin, Raphael Kubo da Costa, Chromium LUCI CQ, android-bu...@system.gserviceaccount.com, chromium...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, hiroshig...@chromium.org, kouhei...@chromium.org, dom+...@chromium.org, chromium-a...@chromium.org, extension...@chromium.org
Attention needed from Camillo Bruni, Jochen Eisinger and Kentaro Hara

Devlin Cronin added 15 comments

Patchset-level comments
Devlin Cronin . resolved

Thanks, Jochen! This is cool!

File extensions/renderer/dispatcher.cc
Line 386, Patchset 5 (Latest):void Dispatcher::OnRenderThreadStarted(content::RenderThread* thread) {}
Devlin Cronin . unresolved

Seems like we can get rid of this method now?

File extensions/renderer/safe_builtins.h
Line 57, Patchset 5 (Latest): const std::unordered_map<std::string, v8::Global<v8::Function>>&
Devlin Cronin . unresolved

we use this type a fair amount. Can we alias it with `using`?

File extensions/renderer/safe_builtins.cc
Line 107, Patchset 5 (Latest): if (!data->Get(context, 0).ToLocal(&target_val) ||
!target_val->IsFunction() || !data->Get(context, 1).ToLocal(&recv)) {
return;
}
Devlin Cronin . unresolved

these are the passed in function and prototype from the safe builtin, passed as a data -- can they ever fail? Or should these be ToChecked()? Ditto below

Line 154, Patchset 5 (Latest): for (const auto& item : saved) {
Devlin Cronin . unresolved

this can potentially throw v8 exceptions if the prototype were tampered with. For one, those probably *shouldn't* be surfaced to the calling script, but also, I think v8 might not allow calling some methods with pending exceptions?

Should we insert a TryCatch here?

Line 178, Patchset 5 (Latest): item.proto->DefineProperty(context, to_json_key, desc);
Devlin Cronin . unresolved

I think defining a property like this will override any previous writable, enumerable, configurable attributes that may have existed. Should we cache those and restore them properly?

Line 142, Patchset 5 (Latest): struct SavedToJSON {
v8::Local<v8::Object> proto;
bool had_own_property = false;
v8::Local<v8::Value> original_descriptor;
};
struct ToJSONRestorer {
v8::Local<v8::Context> context;
v8::Local<v8::String> to_json_key;
std::vector<SavedToJSON> saved;

~ToJSONRestorer() {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
for (const auto& item : saved) {
std::ignore = item.proto->Delete(context, to_json_key);
if (item.had_own_property && !item.original_descriptor.IsEmpty() &&
item.original_descriptor->IsObject()) {
v8::Local<v8::Object> desc_obj =
item.original_descriptor.As<v8::Object>();
v8::Local<v8::Value> get_fn, set_fn, val_val;
bool has_get =
desc_obj
->Get(context, v8_helpers::ToV8StringUnsafe(isolate, "get"))
.ToLocal(&get_fn) &&
!get_fn->IsUndefined();
bool has_set =
desc_obj
->Get(context, v8_helpers::ToV8StringUnsafe(isolate, "set"))
.ToLocal(&set_fn) &&
!set_fn->IsUndefined();
if (has_get || has_set) {
v8::PropertyDescriptor desc(
get_fn.IsEmpty() ? v8::Undefined(isolate).As<v8::Value>()
: get_fn,
set_fn.IsEmpty() ? v8::Undefined(isolate).As<v8::Value>()
: set_fn);
std::ignore =
item.proto->DefineProperty(context, to_json_key, desc);
} else if (desc_obj
->Get(context,
v8_helpers::ToV8StringUnsafe(isolate, "value"))
.ToLocal(&val_val)) {
v8::PropertyDescriptor desc(val_val);
std::ignore =
item.proto->DefineProperty(context, to_json_key, desc);
}
}
}
}
} restorer{context, v8_helpers::ToV8StringUnsafe(isolate, "toJSON")};
Devlin Cronin . unresolved

please add a comment for what this code is doing to help readability

Line 193, Patchset 5 (Latest): for (uint32_t i = 0; i < count; ++i) {
Devlin Cronin . unresolved

this loop as well, please add a brief comment

Line 243, Patchset 5 (Latest): std::ignore =
Devlin Cronin . unresolved

this ignore seems a bit dangerous.

As above, I think we should at least catch any errors that come here. But more than that, it means we'll use the overridden toJSON method (which we also know is *not* the same as the original). Should we just abort in that case perhaps?

Line 280, Patchset 5 (Latest): const std::vector<const char*>& proto_methods,
Devlin Cronin . unresolved

nit: would it make sense for these to be span<>s to avoid allocation of a full array?

Line 285, Patchset 5 (Latest): ctor_val->IsFunction()) {
Devlin Cronin . unresolved

Throughout this function: this operates on the untainted context, right? Are there times fetching these types of builtins could fail? If so, when?

Line 490, Patchset 5 (Latest): if (safe_array_.IsEmpty()) {
v8::Isolate* isolate = context_->isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> v8_context = context_->v8_context();
v8::Context::Scope context_scope(v8_context);

v8::Local<v8::Function> ctor;
if (!untainted_->array.ctor.IsEmpty()) {
ctor = untainted_->array.ctor.Get(isolate);
}

v8::Local<v8::Object> safe =
CreateSafeBuiltin(ctor, untainted_->array.proto_methods,
untainted_->array.static_methods);
safe_array_.Reset(isolate, safe);
}
return safe_array_.Get(context_->isolate());
Devlin Cronin . unresolved

this seems to be very heavily duplicated across most of the other Get() methods for function, object, etc. Can we extract the common bits out to a helper method?

File extensions/renderer/safe_builtins_unittest.cc
Line 80, Patchset 5 (Latest): "Array.prototype.push = function() { throw new Error('clobbered'); };\n"
Devlin Cronin . unresolved

nit: I think string literals would help readability here (avoiding all the \n and new quotes)

Line 116, Patchset 5 (Latest):TEST_F(SafeBuiltinsUnittest, TestAllSafeBuiltinsExposed) {
Devlin Cronin . unresolved

I'm not entirely opposed to these, but they seem very verbose and excessive, and could just become change-detector tests. Is there value in exercising each of the individual bits here explicitly, or should we just test a few?

File extensions/renderer/script_context.h
Line 338, Patchset 5 (Latest): raw_ptr<v8::Isolate> isolate_;
Devlin Cronin . unresolved

add a note that this needs to go above safe_builtins_

Open in Gerrit

Related details

Attention is currently required from:
  • Camillo Bruni
  • Jochen Eisinger
  • Kentaro Hara
Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I90dc32553e5c12d1804dce4592c72642baaf92e2
    Gerrit-Change-Number: 8063487
    Gerrit-PatchSet: 5
    Gerrit-Owner: Jochen Eisinger <joc...@chromium.org>
    Gerrit-Reviewer: Camillo Bruni <cbr...@chromium.org>
    Gerrit-Reviewer: Devlin Cronin <rdevlin...@chromium.org>
    Gerrit-Reviewer: Jochen Eisinger <joc...@chromium.org>
    Gerrit-Reviewer: Kentaro Hara <har...@chromium.org>
    Gerrit-CC: Raphael Kubo da Costa <ku...@igalia.com>
    Gerrit-Attention: Jochen Eisinger <joc...@chromium.org>
    Gerrit-Attention: Kentaro Hara <har...@chromium.org>
    Gerrit-Attention: Camillo Bruni <cbr...@chromium.org>
    Gerrit-Comment-Date: Thu, 09 Jul 2026 18:49:25 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy
    Reply all
    Reply to author
    Forward
    0 new messages