-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Core - Support Chrome command execution. #5257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| // Copyright © 2026 The CefSharp Authors. All rights reserved. | ||
| // | ||
| // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. | ||
|
|
||
| #include "Stdafx.h" | ||
|
|
||
| #include "CefIdMappers.h" | ||
| #include "include\cef_id_mappers.h" | ||
| #include <msclr/marshal_cppstd.h> | ||
|
|
||
| using namespace msclr::interop; | ||
|
|
||
| namespace CefSharp | ||
| { | ||
| namespace Core | ||
| { | ||
| int CefIdMappers::CefIdForCommandIdName(String^ name) | ||
| { | ||
| if (String::IsNullOrEmpty(name)) | ||
| { | ||
| return -1; | ||
| } | ||
|
|
||
| std::string nativeString = marshal_as<std::string>(name); | ||
|
|
||
| return cef_id_for_command_id_name(nativeString.c_str()); | ||
| } | ||
| }; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| // Copyright © 2026 The CefSharp Authors. All rights reserved. | ||
| // | ||
| // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "Stdafx.h" | ||
|
|
||
| namespace CefSharp | ||
| { | ||
| namespace Core | ||
| { | ||
| /// <exclude /> | ||
| [System::ComponentModel::EditorBrowsableAttribute(System::ComponentModel::EditorBrowsableState::Never)] | ||
| public ref class CefIdMappers sealed | ||
| { | ||
| public: | ||
| static int CefIdForCommandIdName(String^ name); | ||
| }; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -639,6 +639,25 @@ void CefBrowserHostWrapper::SetAudioMuted(bool mute) | |
| _browserHost->SetAudioMuted(mute); | ||
| } | ||
|
|
||
| bool CefBrowserHostWrapper::CanExecuteChromeCommand(int commandId) | ||
| { | ||
| ThrowIfDisposed(); | ||
|
|
||
| if (CefCurrentlyOn(TID_UI)) | ||
| { | ||
| return _browserHost->CanExecuteChromeCommand(commandId); | ||
| } | ||
|
|
||
| throw gcnew InvalidOperationException("This method can only be called directly on the CEF UI Thread. Use CanExecuteChromeCommandAsync or use Cef.UIThreadTaskFactory to marshal the call onto the CEF UI Thread."); | ||
| } | ||
|
|
||
| void CefBrowserHostWrapper::ExecuteChromeCommand(int commandId, WindowOpenDisposition disposition) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typically we'd create an enum from the command Id's to make it more user friendly, obviously that has some downsides, needs to be kept up to date, though I wonder how often commands change. I think it's more user friendly than exposing CEF's string to int conversion method.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've actually considered manually mapping the command IDs to an enum initially before discovering However, looking at the Chromium github mirror, these IDs are updated and changed almost every week or two. An enum is definitely more user-friendly, so I've implemented It can be used by (int)ChromeCommandIds.IDC_RELOAD |
||
| { | ||
| ThrowIfDisposed(); | ||
|
|
||
| _browserHost->ExecuteChromeCommand(commandId, (cef_window_open_disposition_t)disposition); | ||
| } | ||
|
Comment on lines
+654
to
+659
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Yes, the CefBrowserHost::ExecuteChromeCommand method must be called on the CEF UI thread [1]. According to the official Chromium Embedded Framework (CEF) documentation, this method specifically requires execution on the UI thread [1]. While many methods in the CefBrowserHost class may be called on any thread in the browser process unless otherwise indicated, ExecuteChromeCommand is explicitly documented with this thread-affinity requirement [1]. If your code is executing on a different thread, you must marshal the task to the UI thread, typically by using CefPostTask with the TID_UI thread identifier [2][3]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
file="CefSharp.Core.Runtime/Internals/CefBrowserHostWrapper.cpp"
# Show the relevant sections around ExecuteChromeCommand / CanExecuteChromeCommand
sed -n '630,720p' "$file" | nl -ba | sed -n '1,120p'
# Show the TryCloseBrowser mentioned in the comment for comparison (line ~158)
sed -n '120,220p' "$file" | nl -ba | sed -n '1,160p'
# Search for the helper used for non-UI thread checks
rg -n "ThrowIfExecutedOnNonCefUiThread|ExecutedOnNonCefUiThread|NonCefUiThread" "$file" || trueRepository: cefsharp/CefSharp Length of output: 104 🌐 Web query:
💡 Result: Yes, CefBrowserHost::ExecuteChromeCommand must be called on the UI thread [1][2]. Official CEF documentation and implementation source code consistently specify that this method is intended for use with the Chrome runtime and has a strict UI thread requirement [1][2][3]. If you need to call this method from a different thread, you must use CEF task posting mechanisms to dispatch the call to the UI thread [3]. For example, in C++ you can use the following approach: if (!CefCurrentlyOn(TID_UI)) { CefPostTask(TID_UI, base::BindOnce(&CefBrowserHost::ExecuteChromeCommand, host, command_id, disposition)); return; } host->ExecuteChromeCommand(command_id, disposition); This pattern ensures that the command is executed safely within the context of the UI thread [3]. Failing to adhere to this requirement may result in threading violations or undefined behavior [4][3]. Citations:
🌐 Web query:
💡 Result: In CefSharp, the method CefBrowserHostWrapper::TryCloseBrowser calls ThrowIfExecutedOnNonCefUiThread to enforce that the operation is performed on the CEF UI thread [1]. This restriction exists because many Chromium Embedded Framework (CEF) browser host operations are thread-sensitive and must be executed on the thread managed by CEF [2][3]. The specific reference to "line 158" in your query likely pertains to a version-specific implementation of the CefBrowserHostWrapper.cpp source file [1]. As codebase lines shift with updates, the exact location of this check may vary across different versions of CefSharp, but the underlying requirement remains consistent: 1. Thread Requirement: The IBrowserHost.TryCloseBrowser method must be called on the CEF UI thread [3]. 2. Enforcement: The wrapper class uses ThrowIfExecutedOnNonCefUiThread to verify the calling thread context [1]. If the method is called from a different thread (e.g., your application's main UI thread, if they differ), an exception will be thrown [1][4]. If you are encountering an exception originating from this check, you must ensure your call to TryCloseBrowser is dispatched to the CEF UI thread [4]. In WinForms or WPF, you can use the appropriate dispatcher or invoke mechanism provided by the browser control to ensure the code executes within the correct context [4]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
file="CefSharp.Core.Runtime/Internals/CefBrowserHostWrapper.cpp"
# Print ExecuteChromeCommand + surrounding code with line numbers (no `nl`)
sed -n '630,705p' "$file" | awk '{printf "%6d\t%s\n", NR+629, $0}'
# Print the earlier area to find TryCloseBrowser / thread checks
sed -n '120,220p' "$file" | awk '{printf "%6d\t%s\n", NR+119, $0}'
# Locate thread-check helpers used in this file
rg -n "ThrowIfExecutedOnNonCefUiThread|ExecutedOnNonCefUiThread|CefCurrentlyOn|TID_UI|On(Cef|NonCef).*UiThread" "$file" || true
# Locate CanExecuteChromeCommand region for context
sed -n '600,670p' "$file" | awk '{printf "%6d\t%s\n", NR+599, $0}'Repository: cefsharp/CefSharp Length of output: 8606 🌐 Web query:
💡 Result: Yes, the CefBrowserHost::ExecuteChromeCommand method must be called on the browser process UI thread [1][2][3]. This requirement is explicitly documented in the official Chromium Embedded Framework (CEF) API reference for the CefBrowserHost class [1][2][3]. If you are not currently on the UI thread, you must marshal the call to the UI thread using CefPostTask [4]. For example: CefPostTask(TID_UI, base::BindOnce([](CefRefPtr host) { host->ExecuteChromeCommand(IDC_YOUR_COMMAND, WINDOW_OPEN_DISPOSITION_CURRENT_TAB); }, browser->GetHost)); Always ensure that you are using version-safe mappings for command IDs, typically via the cef_id_for_command_id_name function, when dealing with Chrome style commands [3]. Citations:
Enforce CEF UI-thread for CefBrowserHostWrapper::ExecuteChromeCommand
🤖 Prompt for AI Agents |
||
|
|
||
| IntPtr CefBrowserHostWrapper::GetOpenerWindowHandle() | ||
| { | ||
| ThrowIfDisposed(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.