Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions ChromiumBasedEditors/lib/include/cefview.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#include "./base.h"
#include "./applicationmanager_events.h"

#include <functional>

enum CefViewWrapperType
{
cvwtSimple = 0,
Expand Down Expand Up @@ -148,6 +150,25 @@ class DESKTOP_DECL CCefView

void ExecuteInAllFrames(const std::string& sCode, const bool& isMain = true);

void SendMouseClickEvent(int x, int y, int button, bool mouseUp, int modifiers, int clickCount);
void SendMouseMoveEvent(int x, int y, bool mouseLeave, int modifiers);
void SendMouseWheelEvent(int x, int y, int deltaX, int deltaY, int modifiers);
void SendKeyEvent(int type, int key, int modifiers, const std::wstring& character);

// In-process CDP bridge for the gateway (see cdp-gateway-cli-plan.md in the
// Euro-Office_DesktopEditors superproject). `jsonMessage` must already be a
// complete UTF8-encoded DevTools Protocol request ("id","method","params"), built
// by the caller -- this method does no JSON construction of its own, matching
// CefBrowserHost::SendDevToolsMessage's own contract. `messageId` must match the
// "id" embedded in `jsonMessage`; it's passed separately only because that's the
// key the response is correlated back by. `callback` fires exactly once, on the
// browser process UI thread, with the matching raw JSON response (or ok=false and
// an error string if the browser/view no longer exists). No CEF types appear in
// this signature, keeping CCefView's public surface CEF-detail-free like the rest
// of this class already is.
void SendGatewayDevToolsMessage(const std::string& jsonMessage, int messageId,
std::function<void(bool ok, const std::string& jsonResponseOrError)> callback);

protected:
int m_nId;
CefViewWrapperType m_eWrapperType;
Expand Down
100 changes: 100 additions & 0 deletions ChromiumBasedEditors/lib/src/cefview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/

#include "include/cef_browser.h"
#include "include/cef_devtools_message_observer.h"
#include "include/base/cef_bind.h"
#include "include/base/cef_callback.h"
#include "include/wrapper/cef_closure_task.h"
Expand Down Expand Up @@ -1210,6 +1211,14 @@ class CCefView_Private : public NSEditorApi::IMenuEventDataBase, public IASCFile

CefRefPtr<CefBrowser> GetBrowser() const;

// Gateway in-process CDP bridge (see CCefView::SendGatewayDevToolsMessage in
// cefview.h). Pending callbacks keyed by the DevTools message id they were sent
// with; the observer registration is created lazily on first use and released
// when this view is destroyed.
std::map<int, std::function<void(bool, const std::string&)>> m_mapGatewayDevToolsCallbacks;
CefRefPtr<CefRegistration> m_oGatewayDevToolsRegistration;
void EnsureGatewayDevToolsObserver();

void CheckLockLocalFile()
{
if (!m_oLocalInfo.m_oInfo.m_bIsSaved)
Expand Down Expand Up @@ -5945,6 +5954,97 @@ CefRefPtr<CefBrowser> CCefView_Private::GetBrowser() const
return nullptr;
return m_handler->GetBrowser();
}

// Gateway in-process CDP bridge. Routes DevTools protocol responses back to whichever
// pending callback matches the "id" CefBrowserHost::ExecuteDevToolsMethod/
// SendDevToolsMessage was called with. One observer per CCefView_Private, registered
// lazily -- see CCefView::SendGatewayDevToolsMessage (cefview.h) and
// cdp-gateway-cli-plan.md for why this replaced an earlier external-CDP-port design.
class CGatewayDevToolsObserver : public CefDevToolsMessageObserver
{
public:
explicit CGatewayDevToolsObserver(CCefView_Private* pView) : m_pView(pView) {}

bool OnDevToolsMessage(CefRefPtr<CefBrowser> browser, const void* message, size_t message_size) override
{
// Parse only far enough to read "id" -- the full message is handed back to
// the caller verbatim as JSON text, so GatewayCommandRunner (desktop-apps)
// parses the rest itself with the same QJsonDocument code path it already
// uses for validation/scope handling. Keeps CEF's own JSON value types out
// of the desktop-apps-facing boundary entirely.
const std::string sMessage(static_cast<const char*>(message), message_size);

CefRefPtr<CefValue> oValue = CefParseJSON(sMessage.c_str(), JSON_PARSER_RFC);
if (!oValue || oValue->GetType() != VTYPE_DICTIONARY)
return false;
CefRefPtr<CefDictionaryValue> oDict = oValue->GetDictionary();
if (!oDict || !oDict->HasKey("id"))
return false; // an event notification, not a method result -- not ours to handle

const int nId = oDict->GetInt("id");
if (!m_pView)
return false;

auto it = m_pView->m_mapGatewayDevToolsCallbacks.find(nId);
if (it == m_pView->m_mapGatewayDevToolsCallbacks.end())
return false; // not a message this bridge sent (e.g. from another DevTools session)

auto callback = std::move(it->second);
m_pView->m_mapGatewayDevToolsCallbacks.erase(it);
callback(true, sMessage);
return true;
}

IMPLEMENT_REFCOUNTING(CGatewayDevToolsObserver);

private:
CCefView_Private* m_pView;
};

void CCefView_Private::EnsureGatewayDevToolsObserver()
{
if (m_oGatewayDevToolsRegistration)
return;

CefRefPtr<CefBrowser> pBrowser = GetBrowser();
if (!pBrowser || !pBrowser->GetHost())
return;

m_oGatewayDevToolsRegistration = pBrowser->GetHost()->AddDevToolsMessageObserver(new CGatewayDevToolsObserver(this));
}

void CCefView::SendGatewayDevToolsMessage(const std::string& jsonMessage, int messageId,
std::function<void(bool ok, const std::string& jsonResponseOrError)> callback)
{
if (!m_pInternal)
{
callback(false, "view is being destroyed");
return;
}

CefRefPtr<CefBrowser> pBrowser = m_pInternal->GetBrowser();
if (!pBrowser || !pBrowser->GetHost())
{
callback(false, "browser not available for this view");
return;
}

m_pInternal->EnsureGatewayDevToolsObserver();
m_pInternal->m_mapGatewayDevToolsCallbacks[messageId] = std::move(callback);

const bool bSubmitted = pBrowser->GetHost()->SendDevToolsMessage(jsonMessage.data(), jsonMessage.size());
if (!bSubmitted)
{
auto it = m_pInternal->m_mapGatewayDevToolsCallbacks.find(messageId);
if (it != m_pInternal->m_mapGatewayDevToolsCallbacks.end())
{
auto failedCallback = std::move(it->second);
m_pInternal->m_mapGatewayDevToolsCallbacks.erase(it);
failedCallback(false, "SendDevToolsMessage submission failed (not on UI thread, or malformed message)");
}
}
}

void CCefView_Private::LocalFile_End()
{
if (!m_oConverterToEditor.m_sName.empty())
Expand Down