I started to implementing NavigationDelegate feature in webview_cef packagehave write some code and it’s blocking all URLs so I need help to do this and manage code properly.
Here’s the package code I did
https://drive.google.com/file/d/1pFwxTEO0-Lw-Hf9IW22dCdms_zGNobfZ/view?usp=sharing
I try to implementing callback NavigationDelegate class and method channel to communicate with native but not have deep knowledge about it.
In my code when I use this code it blocks all the URLs that user can click and redirect but I want to implement it like a callback when dart decision for specific URL is prevent the URL should be prevent and also if decision is navigate the URL should be navigated.
But using this code whatever decision I give it blocks al the URLs in short I have to manage this code so it works as expected. I did changes like given and there nothing loaded everything will be intercepted.
Here’s what I did for this:
webview_handler.cc:
bool WebviewHandler::OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool user_gesture,
bool is_redirect) {
CEF_REQUIRE_UI_THREAD();
std::string url = request->GetURL();
// bool shouldAllowNavigation = true;
if (onNavigationRequestEvent) {
std::cout << "URL =======> " << url << std::endl;
bool shouldAllowNavigation = onNavigationRequestEvent(browser->GetIdentifier(), url);
std::cout << "shouldAllowNavigation ===================> " << shouldAllowNavigation << std::endl;
// If the callback explicitly returns false, block the navigation.
if (!shouldAllowNavigation) {
std::cout << "Blocking navigation to: " << url << std::endl;
return true; // Returning true here blocks the navigation.
}
}
// Otherwise, allow navigation by default.
return false;
}
webview_handler.h:
class WebviewHandler : public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefFocusHandler,
public CefLoadHandler,
public CefRequestHandler,
public CefRenderHandler{
public:
//Paint callback
std::function<void(int browserId, const void* buffer, int32_t width, int32_t height)> onPaintCallback;
//cef message event
std::function<bool(int, std::string)> onNavigationRequestEvent;
std::function<void(int browserId, std::string url)> onUrlChangedEvent;
std::function<void(int browserId, std::string title)> onTitleChangedEvent;
std::function<void(int browserId, int type)>onCursorChangedEvent;
std::function<void(int browserId, std::string text)> onTooltipEvent;
std::function<void(int browserId, int level, std::string message, std::string source, int line)>onConsoleMessageEvent;
std::function<void(int browserId, bool editable)> onFocusedNodeChangeMessage;
std::function<void(int browserId, int32_t x, int32_t y)> onImeCompositionRangeChangedMessage;
//webpage message
std::function<void(std::string, std::string, std::string, int browserId, std::string)> onJavaScriptChannelMessage;
explicit WebviewHandler();
~WebviewHandler();
// CefClient methods:
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() override {
return this;
}
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override {
return this;
}
virtual CefRefPtr<CefFocusHandler> GetFocusHandler() override {
return this;
}
virtual CefRefPtr<CefRequestHandler> GetRequestHandler() override {
return this;
}
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() override { return this; }
virtual CefRefPtr<CefRenderHandler> GetRenderHandler() override { return this; }
virtual bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool user_gesture,
bool is_redirect) override;
webview_plugin.cc:
namespace webview_cef {
CefMainArgs mainArgs;
CefRefPtr<WebviewApp> app;
CefString userAgent;
bool isCefInitialized = false;
WebviewPlugin::WebviewPlugin() {
m_handler = new WebviewHandler();
}
WebviewPlugin::~WebviewPlugin() {
uninitCallback();
m_handler->CloseAllBrowsers(true);
m_handler = nullptr;
if(!m_renderers.empty()){
m_renderers.clear();
}
}
void WebviewPlugin::initCallback() {
if (!m_init)
{
m_handler->onNavigationRequestEvent = [=](int browserId, std::string url) {
if (m_invokeFuncWithReturn) {
std::cout << "C++ Received URL: " << url << std::endl;
WValue* bId = webview_value_new_int(browserId);
WValue* wUrl = webview_value_new_string(const_cast<char*>(url.c_str()));
WValue* retMap = webview_value_new_map();
webview_value_set_string(retMap, "browserId", bId);
webview_value_set_string(retMap, "url", wUrl);
WValue* result = m_invokeFuncWithReturn("onNavigationRequest", retMap); // Send request to Dart
bool shouldAllow = webview_value_get_bool(result);
webview_value_unref(bId);
webview_value_unref(wUrl);
webview_value_unref(retMap);
return shouldAllow; // Allow by default if no decision from Dart
}
return false; // Block navigation if no Dart callback
};
m_init = true;
}
}
void WebviewPlugin::uninitCallback(){
m_handler->onPaintCallback = nullptr;
m_handler->onTooltipEvent = nullptr;
m_handler->onCursorChangedEvent = nullptr;
m_handler->onConsoleMessageEvent = nullptr;
m_handler->onUrlChangedEvent = nullptr;
m_handler->onNavigationRequestEvent = nullptr;
m_handler->onTitleChangedEvent = nullptr;
m_handler->onJavaScriptChannelMessage = nullptr;
m_handler->onFocusedNodeChangeMessage = nullptr;
m_handler->onImeCompositionRangeChangedMessage = nullptr;
m_init = false;
}
void WebviewPlugin::HandleMethodCall(std::string name, WValue* values, std::function<void(int ,WValue*)> result) {
if (name.compare("setNavigationDelegate") == 0) {
int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0)));
const auto url = webview_value_get_string(webview_value_get_list_value(values, 1));
m_handler->onNavigationRequestEvent(browserId,url);
// setNavigationRequestCallback();
result(1, nullptr); // Respond to Dart
}
// else if (name.compare("setNavigationDelegate") == 0) {
// // Set up navigation delegate
// m_handler->onNavigationRequestEvent = [=](int browserId, std::string url) {
// std::cout << "C++ Received URL: " << url << std::endl;
//
// // Call Dart side via the channel
// if (m_invokeFunc) {
// WValue* bId = webview_value_new_int(browserId);
// WValue* wUrl = webview_value_new_string(const_cast<char*>(url.c_str()));
// WValue* retMap = webview_value_new_map();
// webview_value_set_string(retMap, "browserId", bId);
// webview_value_set_string(retMap, "url", wUrl);
//
// // Send navigation request event to Dart
// m_invokeFunc("onNavigationRequest", retMap);
//
// webview_value_unref(bId);
// webview_value_unref(wUrl);
// webview_value_unref(retMap);
//
// // For now, allow navigation by default until Dart responds
// return true; // Allow navigation by default
// }
// return false; // Block navigation if Dart doesn't respond
// };
// result(1, nullptr); // Respond to Dart that setup is complete
// }
else {
result = 0;
}
}
void WebviewPlugin::setInvokeMethodFunc(std::function<void(std::string, WValue*)> func){
m_invokeFunc = func;
}
void WebviewPlugin::setInvokeMethodFuncWithReturn(std::function<WValue * (std::string, WValue *)> func) {
m_invokeFuncWithReturn = func;
}
void WebviewPlugin::setNavigationRequestCallback(std::function<bool(int, std::string)> callback) {
m_navigationRequestCallback = [callback](int browserId, std::string url) -> bool {
if (callback) {
return callback(browserId, url);
}
return true; // Allow navigation by default
};
}
webview_plugin.h:
#ifndef WEBVIEW_PLUGIN_H
#define WEBVIEW_PLUGIN_H
#include "webview_value.h"
#include "webview_app.h"
#include <include/cef_base.h>
#include <functional>
namespace webview_cef {
class WebviewTexture{
public:
virtual ~WebviewTexture(){}
virtual void onFrame(const void* buffer, int width, int height){}
int64_t textureId = 0;
bool isFocused = false;
};
class WebviewPlugin {
public:
WebviewPlugin();
~WebviewPlugin();
void initCallback();
void uninitCallback();
void HandleMethodCall(std::string name, WValue* values, std::function<void(int ,WValue*)> result);
void sendKeyEvent(CefKeyEvent& ev);
void setInvokeMethodFunc(std::function<void(std::string, WValue*)> func);
void setInvokeMethodFuncWithReturn(std::function<WValue*(std::string, WValue*)> func); // New function
void setNavigationRequestCallback(std::function<bool(int, std::string)> callback);
void setCreateTextureFunc(std::function<std::shared_ptr<WebviewTexture>()> func);
bool getAnyBrowserFocused();
private :
int cursorAction(WValue *args, std::string name);
std::function<void(std::string, WValue*)> m_invokeFunc;
std::function<WValue*(std::string, WValue*)> m_invokeFuncWithReturn; // New member
std::function<std::shared_ptr<WebviewTexture>()> m_createTextureFunc;
std::function<bool(int, std::string)> m_navigationRequestCallback;
CefRefPtr<WebviewHandler> m_handler;
CefRefPtr<WebviewApp> m_app;
std::unordered_map<int, std::shared_ptr<WebviewTexture>> m_renderers;
bool m_init = false;
};
void initCEFProcesses(CefMainArgs args);
void initCEFProcesses();
void startCEF();
void doMessageLoopWork();
void SwapBufferFromBgraToRgba(void* _dest, const void* _src, int width, int height);
void stopCEF();
}
#endif //WEBVIEW_PLUGIN_H
navigation_delegate.dart:
typedef OnNavigationDecisionCb = NavigationDecision Function(String url);
class NavigationDelegate {
final OnNavigationDecisionCb? onNavigationRequest;
NavigationDelegate({this.onNavigationRequest});
}
enum NavigationDecision {
prevent,
navigate,
}
webview_manager.dart:
Future<bool> navigationCallHandle(MethodCall call) async{
switch (call.method){
case "onNavigationRequest":
int browserId = call.arguments["browserId"] as int;
final String url = call.arguments['url'];
print("Dart side URL: $url");
final decision = _webViews[browserId]?.delegate?.onNavigationRequest!(url);
// if (decision != null) {
print("Navigation decision for $url: $decision");
final shouldBlock = (decision == NavigationDecision.navigate);
return shouldBlock;
// }
}
return true;
}
webview.dart:
setNavigationDelegate(NavigationDelegate delegate){
_delegate = delegate;
}
JEET NADIYADARA is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.