Files
electron/shell/browser/net/client_certificate_responder_delegate.cc
T
David Babenko-SandersandClaude Opus 5.5 6431970d8b build: enable more performance-* clang-tidy checks (#54024)
* build: enable more performance-* clang-tidy checks

* chore: performance-prefer-single-char-overloads

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* chore: performance-unnecessary-copy-initialization

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* chore: performance-use-std-move

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* chore: performance-move-const-arg

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* chore: performance-no-automatic-move

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* chore: address review feedback

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MC6nEgyED2MtseZ9KdGMpt

* chore: fix new clang-tidy performance findings

Address performance-use-std-move, performance-move-const-arg, and
performance-prefer-single-char-overloads findings in code that landed
on main since the previous base.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MC6nEgyED2MtseZ9KdGMpt

* chore: fix new clang-tidy performance findings on macOS

Silence performance-unnecessary-copy-initialization for task runners
captured by Objective-C blocks in the macOS file and message box
dialogs, matching the existing pattern: the copy is needed since a
block would otherwise capture the reference by reference.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MC6nEgyED2MtseZ9KdGMpt

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-23 14:53:40 -07:00

154 lines
5.9 KiB
C++

// Copyright (c) 2026 Anthropic, PBC.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/net/client_certificate_responder_delegate.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/client_certificate_delegate.h"
#include "content/public/common/child_process_id.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/net_errors.h"
#include "net/ssl/client_cert_identity.h"
#include "net/ssl/client_cert_store.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_private_key.h"
#include "services/network/public/mojom/url_loader_network_service_observer.mojom.h"
#include "shell/browser/electron_browser_client.h"
namespace electron {
namespace {
// Mirrors content/browser/ssl_private_key_impl.h (not public) so the network
// service can sign with a key that lives in the browser process.
class SSLPrivateKeyImpl : public network::mojom::SSLPrivateKey {
public:
explicit SSLPrivateKeyImpl(scoped_refptr<net::SSLPrivateKey> ssl_private_key)
: ssl_private_key_(std::move(ssl_private_key)) {}
SSLPrivateKeyImpl(const SSLPrivateKeyImpl&) = delete;
SSLPrivateKeyImpl& operator=(const SSLPrivateKeyImpl&) = delete;
// network::mojom::SSLPrivateKey:
void Sign(uint16_t algorithm,
const std::vector<uint8_t>& input,
SignCallback callback) override {
// `ssl_private_key_` may abandon the callback (https://crbug.com/40651689).
callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
std::move(callback), int32_t{net::ERR_FAILED}, std::vector<uint8_t>());
ssl_private_key_->Sign(algorithm, input,
base::BindOnce(
[](SignCallback callback, net::Error net_error,
const std::vector<uint8_t>& signature) {
std::move(callback).Run(
static_cast<int32_t>(net_error),
signature);
},
std::move(callback)));
}
private:
scoped_refptr<net::SSLPrivateKey> ssl_private_key_;
};
// Adapts the mojo responder to the content::ClientCertificateDelegate
// interface that ElectronBrowserClient::SelectClientCertificate expects.
class ClientCertificateResponderDelegate
: public content::ClientCertificateDelegate {
public:
explicit ClientCertificateResponderDelegate(
mojo::PendingRemote<network::mojom::ClientCertificateResponder> responder)
: responder_(std::move(responder)) {
responder_.set_disconnect_handler(
base::BindOnce(&ClientCertificateResponderDelegate::OnDisconnect,
base::Unretained(this)));
}
~ClientCertificateResponderDelegate() override {
if (!responded_ && responder_)
responder_->CancelRequest();
}
ClientCertificateResponderDelegate(
const ClientCertificateResponderDelegate&) = delete;
ClientCertificateResponderDelegate& operator=(
const ClientCertificateResponderDelegate&) = delete;
// content::ClientCertificateDelegate:
void ContinueWithCertificate(
scoped_refptr<net::X509Certificate> cert,
scoped_refptr<net::SSLPrivateKey> private_key) override {
if (responded_ || !responder_)
return;
responded_ = true;
if (!cert || !private_key) {
responder_->ContinueWithoutCertificate();
return;
}
mojo::PendingRemote<network::mojom::SSLPrivateKey> ssl_private_key;
mojo::MakeSelfOwnedReceiver(
std::make_unique<SSLPrivateKeyImpl>(private_key),
ssl_private_key.InitWithNewPipeAndPassReceiver());
responder_->ContinueWithCertificate(cert, private_key->GetProviderName(),
private_key->GetAlgorithmPreferences(),
std::move(ssl_private_key));
}
private:
void OnDisconnect() { responder_.reset(); }
bool responded_ = false;
mojo::Remote<network::mojom::ClientCertificateResponder> responder_;
};
void OnGotClientCerts(
std::unique_ptr<net::ClientCertStore> cert_store,
std::unique_ptr<content::ClientCertificateDelegate> delegate,
content::BrowserContext* browser_context,
scoped_refptr<net::SSLCertRequestInfo> cert_info,
net::ClientCertIdentityList identities) {
// ElectronBrowserClient handles both the empty-list case (continue without a
// certificate) and routing to the `select-client-certificate` app event.
ElectronBrowserClient::Get()->SelectClientCertificate(
browser_context, /*process_id=*/content::ChildProcessId(),
/*web_contents=*/nullptr, cert_info.get(), std::move(identities),
std::move(delegate));
}
} // namespace
void SelectClientCertificateForResponder(
content::BrowserContext* browser_context,
scoped_refptr<net::SSLCertRequestInfo> cert_info,
mojo::PendingRemote<network::mojom::ClientCertificateResponder>
cert_responder) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto delegate = std::make_unique<ClientCertificateResponderDelegate>(
std::move(cert_responder));
std::unique_ptr<net::ClientCertStore> cert_store =
ElectronBrowserClient::Get()->CreateClientCertStore(browser_context);
if (!cert_store) {
delegate->ContinueWithCertificate(nullptr, nullptr);
return;
}
// The store must outlive the async lookup; bind it into the callback.
auto* cert_store_ptr = cert_store.get();
cert_store_ptr->GetClientCerts(
cert_info,
base::BindOnce(&OnGotClientCerts, std::move(cert_store),
std::move(delegate), browser_context, cert_info));
}
} // namespace electron