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
34 changes: 34 additions & 0 deletions code/framework/src/gui/cef/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,28 @@
*/

#include "app.h"
#include "include/cef_parser.h"

namespace Framework::GUI::CEF {

CefRefPtr<CefResourceHandler> App::Create(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString &scheme_name, CefRefPtr<CefRequest> request) {
if (!browser || !frame)
return nullptr;

CefURLParts urlParts;
if (!CefParseURL(request->GetURL(), urlParts))
return nullptr;

std::string scheme = CefString(&urlParts.scheme).ToString();
std::string domain = CefString(&urlParts.host).ToString();

auto it = _handlers.find({scheme, domain});
if (it == _handlers.end())
return nullptr;
Comment on lines +25 to +27
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Protect _handlers with synchronization (read/write race).

Create() reads _handlers while RegisterSchemeHandlerFactory() mutates it with no lock. If registration can happen after CEF starts handling requests, this is a data race on std::unordered_map (undefined behavior).

Suggested fix
 // app.h
+#include <shared_mutex>
 ...
 private:
+    mutable std::shared_mutex _handlersMutex;
     std::unordered_map<SchemeDomainKey, Framework::GUI::CEF::SchemaHandlerFactoryCallback, KeyHash> _handlers;

 // app.cpp
-    auto it = _handlers.find({scheme, domain});
+    std::shared_lock lock(_handlersMutex);
+    auto it = _handlers.find({scheme, domain});
     if (it == _handlers.end())
         return nullptr;

-    _handlers[{scheme, domain}] = callback;
+    {
+        std::unique_lock lock(_handlersMutex);
+        _handlers[{scheme, domain}] = std::move(callback);
+    }

Also applies to: 55-57

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/framework/src/gui/cef/app.cpp` around lines 25 - 27, The code reads from
and mutates the unordered_map _handlers concurrently (in Create() and
RegisterSchemeHandlerFactory()), causing a data race; protect _handlers with a
mutex (prefer std::shared_mutex) and acquire a shared/read lock when accessing
in Create() (and the similar read at lines ~55-57) and an exclusive/write lock
when modifying it in RegisterSchemeHandlerFactory() so all reads/writes are
synchronized and undefined behavior is avoided.


return it->second(browser, frame, scheme_name, request);
}

void App::OnBeforeCommandLineProcessing(const CefString &processType, CefRefPtr<CefCommandLine> commandLine) {
commandLine->AppendSwitch("disable-gpu-compositing");
commandLine->AppendSwitch("disable-extensions");
Expand All @@ -21,4 +41,18 @@ namespace Framework::GUI::CEF {
void App::OnContextInitialized() {
_contextInitialized = true;
}

void App::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) {
// In --single-process mode the renderer runs inside the browser process,
// so App (not RendererApp) receives this callback. Register the same
// JS bindings that RendererApp::OnContextCreated registers in multi-process mode.
CefRefPtr<CefV8Value> global = context->GetGlobal();
CefRefPtr<CefV8Handler> handler = new CallEventHandler(browser);
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("callEvent", handler);
global->SetValue("callEvent", func, V8_PROPERTY_ATTRIBUTE_NONE);
}

void App::RegisterSchemeHandlerFactory(const std::string &scheme, const std::string &domain, SchemaHandlerFactoryCallback callback) {
_handlers[{scheme, domain}] = callback;
}
} // namespace Framework::GUI::CEF
34 changes: 33 additions & 1 deletion code/framework/src/gui/cef/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,33 @@
#include "include/cef_app.h"
#include "include/cef_browser_process_handler.h"

#include <function2.hpp>
#include <unordered_map>

namespace Framework::GUI::CEF {
class App final: public CefApp, public CefBrowserProcessHandler {

using SchemaHandlerFactoryCallback = fu2::function<CefRefPtr<CefResourceHandler>(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString &scheme_name, CefRefPtr<CefRequest> request) const>;

struct SchemeDomainKey {
std::string scheme;
std::string domain;

bool operator==(const SchemeDomainKey &o) const {
return scheme == o.scheme && domain == o.domain;
}
};

struct KeyHash {
size_t operator()(const SchemeDomainKey &k) const {
return std::hash<std::string> {}(k.scheme) ^ (std::hash<std::string> {}(k.domain) << 1);
}
};

class App final
: public CefApp
, public CefBrowserProcessHandler
, public CefRenderProcessHandler
, public CefSchemeHandlerFactory {
private:
bool _contextInitialized = false;

Expand All @@ -28,6 +53,13 @@ namespace Framework::GUI::CEF {
return _contextInitialized;
}

virtual CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString &scheme_name, CefRefPtr<CefRequest> request) override;

void RegisterSchemeHandlerFactory(const std::string &schema, const std::string &domain, Framework::GUI::CEF::SchemaHandlerFactoryCallback callback);

IMPLEMENT_REFCOUNTING(App);

private:
std::unordered_map<SchemeDomainKey, Framework::GUI::CEF::SchemaHandlerFactoryCallback, KeyHash> _handlers;
};
} // namespace Framework::GUI::CEF
4 changes: 4 additions & 0 deletions code/framework/src/gui/cef/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ namespace Framework::GUI::CEF {
return _displayHandler;
}

CefRefPtr<CefRequestHandler> GetRequestHandler() override {
return _lifeSpanHandler;
}

bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefProcessId sourceProcess, CefRefPtr<CefProcessMessage> message) override;

IMPLEMENT_REFCOUNTING(Client);
Expand Down
13 changes: 13 additions & 0 deletions code/framework/src/gui/cef/life_span_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

#include "life_span_handler.h"
#include "include/cef_parser.h"

namespace Framework::GUI::CEF {
void LifeSpanHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
Expand All @@ -21,4 +22,16 @@ namespace Framework::GUI::CEF {
void LifeSpanHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
_browser = nullptr;
}

bool LifeSpanHandler::OnBeforeBrowse(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, bool userGesture, bool isRedirect) {
CefURLParts urlParts;
if (!CefParseURL(request->GetURL(), urlParts))
return true; // Cancel if invalid URL

if (_onBeforeBrowse) {
return _onBeforeBrowse(urlParts, browser, frame, request, userGesture, isRedirect);
}
return false;

}
} // namespace Framework::GUI::CEF
19 changes: 18 additions & 1 deletion code/framework/src/gui/cef/life_span_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,33 @@
#pragma once

#include "include/cef_life_span_handler.h"
#include "include/cef_request_handler.h"
#include <functional>

namespace Framework::GUI::CEF {
class LifeSpanHandler final: public CefLifeSpanHandler {
using OnBeforeBrowseCallback = std::function<bool(CefURLParts, CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>, CefRefPtr<CefRequest>, bool, bool)>;

class LifeSpanHandler final
: public CefLifeSpanHandler
, public CefRequestHandler {
private:
CefRefPtr<CefBrowser> _browser;

std::function<void(CefRefPtr<CefBrowser>)> _onAfterCreated;
OnBeforeBrowseCallback _onBeforeBrowse;

public:
void SetOnAfterCreatedCallback(std::function<void(CefRefPtr<CefBrowser>)> cb) {
_onAfterCreated = std::move(cb);
}
void SetOnBeforeBrowseCallback(OnBeforeBrowseCallback cb) {
_onBeforeBrowse = std::move(cb);
}

void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
bool OnBeforePopup(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int popupId, const CefString &targetUrl, const CefString &targetFrameName, CefLifeSpanHandler::WindowOpenDisposition targetDisposition, bool userGesture, const CefPopupFeatures &popupFeatures, CefWindowInfo &windowInfo, CefRefPtr<CefClient> &client, CefBrowserSettings &settings, CefRefPtr<CefDictionaryValue> &extraInfo, bool *noJavascriptAccess) override;
void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, bool userGesture, bool isRedirect) override;

CefRefPtr<CefBrowser> GetBrowser() const {
return _browser;
Expand Down
5 changes: 5 additions & 0 deletions code/framework/src/gui/manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,9 @@ namespace Framework::GUI {
}
return nullptr;
}

void Manager::RegisterSchemeHandlerFactory(const std::string &schema, const std::string &domain, Framework::GUI::CEF::SchemaHandlerFactoryCallback callback) {
_cefApp->RegisterSchemeHandlerFactory(schema, domain, callback);
CefRegisterSchemeHandlerFactory(schema, domain, _cefApp);
}
Comment on lines +254 to +257
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and print the relevant region around the flagged lines
FILE="code/framework/src/gui/manager.cpp"

# Show method and nearby members/flags
echo "=== Context around RegisterSchemeHandlerFactory ==="
nl -ba "$FILE" | sed -n '220,320p'

echo
echo "=== Search for CEF init guards / flags in this file ==="
rg -n "_cefInitialized|_cefApp|Init\(|RegisterSchemeHandlerFactory" "$FILE"

echo
echo "=== Find definitions/usage of _cefApp and CEF init state ==="
rg -n "CefRegisterSchemeHandlerFactory" "$FILE"
rg -n "RegisterSchemeHandlerFactory\(" "$FILE"

echo
echo "=== Find other call sites for RegisterSchemeHandlerFactory in repo ==="
rg -n "RegisterSchemeHandlerFactory\s*\(" -S code | head -n 50

echo
echo "=== Check how _cefInitialized is set / what _cefInitialized means ==="
rg -n "_cefInitialized\s*=" -S code/framework/src/gui/manager.cpp

Repository: MafiaHub/Framework

Length of output: 175


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="code/framework/src/gui/manager.cpp"

echo "=== Verify file exists ==="
ls -la "$FILE" || true

echo
echo "=== Context around RegisterSchemeHandlerFactory (lines 240-290) ==="
awk 'NR>=240 && NR<=290 {printf "%d:%s\n", NR, $0}' "$FILE" || true

echo
echo "=== Search for init guard / members in this file ==="
rg -n "_cefInitialized|_cefApp|Init\(|RegisterSchemeHandlerFactory" "$FILE" || true

echo
echo "=== Find usages of CEF registration return value ==="
rg -n "CefRegisterSchemeHandlerFactory" "$FILE" || true

echo
echo "=== Search for other call sites in repo ==="
rg -n "RegisterSchemeHandlerFactory\s*\(" -S code | head -n 50 || true

echo
echo "=== Where is _cefInitialized defined/set in this file ==="
rg -n "_cefInitialized" "$FILE" || true

Repository: MafiaHub/Framework

Length of output: 3392


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect Manager ctor/dtor and Init logic
FILE="code/framework/src/gui/manager.cpp"
echo "=== Manager.cpp: lines 1-120 ==="
awk 'NR>=1 && NR<=120 {printf "%d:%s\n", NR, $0}' "$FILE"

echo
echo "=== Manager.cpp: lines 120-220 ==="
awk 'NR>=120 && NR<=220 {printf "%d:%s\n", NR, $0}' "$FILE"

echo
echo "=== Inspect Manager header for members (_cefApp, _cefInitialized) ==="
HFILE="code/framework/src/gui/manager.h"
nl -ba "$HFILE" | sed -n '1,140p'

echo
echo "=== Inspect CEF app wrapper header ==="
AHD="code/framework/src/gui/cef/app.h"
nl -ba "$AHD" | sed -n '1,140p'

echo
echo "=== Inspect CEF app wrapper impl around RegisterSchemeHandlerFactory ==="
ACI="code/framework/src/gui/cef/app.cpp"
awk 'NR>=1 && NR<=120 {printf "%d:%s\n", NR, $0}' "$ACI"

echo
echo "=== Inspect CEF app wrapper impl further (around line 55 from rg output) ==="
awk 'NR>=40 && NR<=110 {printf "%d:%s\n", NR, $0}' "$ACI"

Repository: MafiaHub/Framework

Length of output: 7782


🏁 Script executed:

#!/bin/bash
set -euo pipefail

MFILE="code/framework/src/gui/manager.cpp"
HFILE="code/framework/src/gui/manager.h"
AHD="code/framework/src/gui/cef/app.h"
ACI="code/framework/src/gui/cef/app.cpp"

echo "=== Search for IsInitialized() ==="
rg -n "IsInitialized\s*\(" "$MFILE" "$HFILE"

echo
echo "=== Show Manager header member declarations (look for _cefApp/_cefInitialized) ==="
awk 'NR>=1 && NR<=140 {print NR ":" $0}' "$HFILE" | rg -n "_cefApp|_cefInitialized|_initialized|IsInitialized" || true

echo
echo "=== Show Manager header around those member declarations (wider context) ==="
rg -n "_cefApp|_cefInitialized|_initialized" "$HFILE" || true
LINE=$(rg -n "_cefApp|_cefInitialized|_initialized" "$HFILE" | head -n1 | cut -d: -f1)
START=$((LINE-20)); END=$((LINE+60))
awk -v s=$START -v e=$END 'NR>=s && NR<=e {print NR ":" $0}' "$HFILE" || true

echo
echo "=== Inspect CEF app wrapper declarations ==="
awk 'NR>=1 && NR<=140 {print NR ":" $0}' "$AHD" | rg -n "RegisterSchemeHandlerFactory|class App|_cefApp|CefRegisterSchemeHandlerFactory" || true

echo
echo "=== Inspect CEF app wrapper implementation around RegisterSchemeHandlerFactory ==="
rg -n "RegisterSchemeHandlerFactory" "$ACI"
MATCH_LINE=$(rg -n "RegisterSchemeHandlerFactory" "$ACI" | head -n1 | cut -d: -f1)
START=$((MATCH_LINE-20)); END=$((MATCH_LINE+60))
awk -v s=$START -v e=$END 'NR>=s && NR<=e {print NR ":" $0}' "$ACI"

Repository: MafiaHub/Framework

Length of output: 4601


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for declaration/signature of CefRegisterSchemeHandlerFactory in repo headers (if vendored)
rg -n "CefRegisterSchemeHandlerFactory\s*\(" code/framework/src/gui -S || true
rg -n "CefRegisterSchemeHandlerFactory\s*\(" code -S || true

# Try to find CEF headers under include/ or third_party if present
fd -i "cef*.h" code | head -n 50
# Search likely header files for the signature (heuristic)
for f in $(fd -i "cef*.h" code 2>/dev/null | head -n 200); do
  if rg -n "CefRegisterSchemeHandlerFactory" "$f" >/dev/null 2>&1; then
    echo "=== Found in $f ==="
    rg -n "CefRegisterSchemeHandlerFactory" "$f" | head -n 20
  fi
done

Repository: MafiaHub/Framework

Length of output: 421


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find call sites for Manager::RegisterSchemeHandlerFactory
rg -n "RegisterSchemeHandlerFactory\s*\(" code -S

# Narrow to likely member call syntax
rg -n "\.RegisterSchemeHandlerFactory\s*\(" code -S || true
rg -n "Manager::RegisterSchemeHandlerFactory\s*\(" code -S || true

# Also search for IsInitialized checks near potential calls
rg -n "IsInitialized\s*\(|_cefInitialized\s*|GUI_CEF_INIT_FAILED" code -S | head -n 200

Repository: MafiaHub/Framework

Length of output: 4640


🏁 Script executed:

#!/bin/bash
set -euo pipefail
HFILE="code/framework/src/gui/cef/app.h"

echo "=== CEF App class declaration (search for CefSchemeHandlerFactory / inheritance) ==="
rg -n "class App|CefSchemeHandlerFactory|SchemeHandlerFactory|RegisterSchemeHandlerFactory" "$HFILE"

echo
echo "=== Show app.h around class App definition ==="
LINE=$(rg -n "class App" "$HFILE" | head -n1 | cut -d: -f1)
START=$((LINE-40)); END=$((LINE+120))
awk -v s=$START -v e=$END 'NR>=s && NR<=e {print NR ":" $0}' "$HFILE"

Repository: MafiaHub/Framework

Length of output: 2858


🌐 Web query:

CefRegisterSchemeHandlerFactory return type bool CefSchemeHandlerFactory registration

💡 Result:

The CefRegisterSchemeHandlerFactory function in the Chromium Embedded Framework (CEF) has a return type of bool [1][2]. It returns true if the scheme handler factory is successfully registered, and false if an error occurs [3][1]. This function is used to register a scheme handler factory with the global request context [1][4]. Key implementation details include: - Function Signature: In C++, it is defined as: bool CefRegisterSchemeHandlerFactory(const CefString& scheme_name, const CefString& domain_name, CefRefPtr factory) [1][2]. - Behavior: - An empty domain_name value for a standard scheme causes the factory to match all domain names [1][2]. - For non-standard schemes, the domain_name parameter is ignored [1][2]. - If the scheme_name is a custom scheme, you must also implement the CefApp::OnRegisterCustomSchemes method in all processes [1][2]. - The function may be called multiple times to change or remove a previously registered factory [1][2]. - It can be called on any thread in the browser process [1][2]. In CefSharp, the equivalent method RequestContext.RegisterSchemeHandlerFactory also returns a boolean value, indicating success or failure [5][6].

Citations:


Guard CEF scheme handler registration and handle failures explicitly.

  • Manager::RegisterSchemeHandlerFactory dereferences _cefApp without checking _cefInitialized; _cefApp is only created in Init(), so calling this before successful CEF init can dereference null.
  • CefRegisterSchemeHandlerFactory(...) returns bool and its result is ignored, hiding registration failure/duplicate registration issues.
Suggested fix
 void Manager::RegisterSchemeHandlerFactory(const std::string &schema, const std::string &domain, Framework::GUI::CEF::SchemaHandlerFactoryCallback callback) {
+    if (!_cefInitialized || !_cefApp) {
+        Framework::Logging::GetLogger("Web")->error("Cannot register scheme handler factory before CEF initialization");
+        return;
+    }
     _cefApp->RegisterSchemeHandlerFactory(schema, domain, callback);
-    CefRegisterSchemeHandlerFactory(schema, domain, _cefApp);
+    if (!CefRegisterSchemeHandlerFactory(schema, domain, _cefApp)) {
+        Framework::Logging::GetLogger("Web")->error("CefRegisterSchemeHandlerFactory failed for schema='{}' domain='{}'", schema, domain);
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/framework/src/gui/manager.cpp` around lines 254 - 257,
Manager::RegisterSchemeHandlerFactory currently dereferences _cefApp without
checking _cefInitialized and ignores the bool return from
CefRegisterSchemeHandlerFactory; update the method to first check
_cefInitialized and that _cefApp != nullptr (and return or report an error if
not initialized), use the provided
Framework::GUI::CEF::SchemaHandlerFactoryCallback to call
_cefApp->RegisterSchemeHandlerFactory only when safe, call
CefRegisterSchemeHandlerFactory and capture its bool result, and handle a false
return by logging or returning an error indicating registration failed or was
duplicate (include the schema/domain in the message).

} // namespace Framework::GUI
2 changes: 2 additions & 0 deletions code/framework/src/gui/manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,7 @@ namespace Framework::GUI {
}

View *GetView(int id) const;

void RegisterSchemeHandlerFactory(const std::string &schema, const std::string &domain, Framework::GUI::CEF::SchemaHandlerFactoryCallback callback);
};
} // namespace Framework::GUI
4 changes: 4 additions & 0 deletions code/framework/src/gui/view.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,5 +185,9 @@ namespace Framework::GUI {
inline void SetOnWindowObjectReadyCallback(OnWindowObjectReadyCallback proc) {
_onWindowObjectReadyCallback = std::move(proc);
}

inline void SetOnBeforeBrowseCallback(CEF::OnBeforeBrowseCallback cb) {
_lifeSpanHandler->SetOnBeforeBrowseCallback(std::move(cb));
}
Comment on lines +189 to +191
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify where _lifeSpanHandler is initialized relative to SetOnBeforeBrowseCallback call sites.
rg -n "View::View\\(|_lifeSpanHandler\\s*=|SetOnBeforeBrowseCallback\\(" code/framework/src/gui -g '*.{h,hpp,hxx,cpp,cc,cxx}'

Repository: MafiaHub/Framework

Length of output: 604


Guard _lifeSpanHandler (or document Init-before-use) in SetOnBeforeBrowseCallback.

SetOnBeforeBrowseCallback dereferences _lifeSpanHandler directly (_lifeSpanHandler->SetOnBeforeBrowseCallback(...)) without a null check; ensure _lifeSpanHandler is always initialized before this setter can be called (or add a defensive guard).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/framework/src/gui/view.h` around lines 189 - 191,
SetOnBeforeBrowseCallback currently dereferences _lifeSpanHandler without a null
check; either ensure callers cannot call this before initialization or add a
defensive guard: in SetOnBeforeBrowseCallback (the inline method) check
_lifeSpanHandler for null and handle it (e.g., assert/throw/log and return) or
lazily create/initialize the handler before calling
_lifeSpanHandler->SetOnBeforeBrowseCallback(std::move(cb)); update the method to
reference _lifeSpanHandler and the class initialization path for
_lifeSpanHandler to make intent clear.

};
} // namespace Framework::GUI