mirror of
https://github.com/electron/electron.git
synced 2026-10-28 11:44:46 +08:00
* 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>
154 lines
5.9 KiB
C++
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
|