How to open a link in a native browser from CefSharp 3
Asked Answered
T

5

8

I have a requirement to open a link in a native browser from CefSharp 3. I need to run the whole application in CefSharp 3's chromium browser except a form. when I click the form's link button (Eg - Register button. It has a link to the registration form) I need to open this link in the native browser (Eg - Internet Explorer).

Can we achieve this in CefSharp?

I searched the google as well as stack overflow. But unable to find a solution.

Treed answered 6/7, 2015 at 8:39 Comment(0)
T
12

As suggested by holroy I have implemented the OnBeforeNavigation() method in the RequestHandler class in CefSharp.Example package.

This is the working code,

 bool IRequestHandler.OnBeforeBrowse(IWebBrowser browserControl,
 IBrowser browser, IFrame frame, IRequest request, bool isRedirect)
         {
             // If the url is Google open Default browser
             if (request.Url.Equals("http://google.com/"))
             {
                 // Open Google in Default browser 
                 System.Diagnostics.Process.Start("http://google.com/");
                 return true;
             }else
             {
                 // Url except Google open in CefSharp's Chromium browser
                 return false;
             }
         }

I hope this will help to some one else in future.

Thanks,

Treed answered 7/7, 2015 at 8:54 Comment(1)
Note: The function signature has changed. There's a bool userGesture before bool isRedirect now.Irritant
O
5

First you need to create a custom BrowserRequestHandler class such as this:

public class BrowserRequestHandler : IRequestHandler
{
    public bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect)
    {
        // Open in Default browser
        if (!request.Url.StartsWith("file:"))
        {
            System.Diagnostics.Process.Start(request.Url);
            return true;
        }
        return false;
    }

    public bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl,
        WindowOpenDisposition targetDisposition, bool userGesture)
    {
        return false;
    }

    public bool OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl,
        ISslInfo sslInfo, IRequestCallback callback)
    {
        callback.Dispose();
        return false;
    }

    public void OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath)
    {
        throw new Exception("Plugin crashed!");
    }

    public CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request,
        IRequestCallback callback)
    {
        return CefReturnValue.Continue;
    }

    public bool GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port,
        string realm, string scheme, IAuthCallback callback)
    {
        callback.Dispose();
        return false;
    }

    public bool OnSelectClientCertificate(IWebBrowser browserControl, IBrowser browser, bool isProxy, string host, int port,
        X509Certificate2Collection certificates, ISelectClientCertificateCallback callback)
    {
        callback.Dispose();
        return false;
    }

    public void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status)
    {
        throw new Exception("Browser render process is terminated!");
    }

    public bool OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl, long newSize,
        IRequestCallback callback)
    {
        callback.Dispose();
        return false;
    }

    public void OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response,
        ref string newUrl)
    {
        var url = newUrl;
        newUrl = url;
    }

    public bool OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url)
    {
        return url.StartsWith("mailto");
    }

    public void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser)
    {

    }

    public bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)
    {
        return false;
    }

    public IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request,
        IResponse response)
    {
        return null;
    }

    public void OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request,
        IResponse response, UrlRequestStatus status, long receivedContentLength)
    {

    }
}

The important part in this code is:

public bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect)
{
    // Open in Default browser
    if (!request.Url.StartsWith("file:"))
    {
        System.Diagnostics.Process.Start(request.Url);
        return true;
    }
    return false;
}

In my case, I was opening locally saved HTML files in CEF, and if those locally saved HTML files had external links, they would open up in the default browser.

Now that you created the custom BrowserRequestHandler, you need to assign it to the browser.

If you are using MVVM, you can create a BrowserRequestHandler in your model and assign it to the RequestHandlerdependency property of the browser control. But in my case there were timing issues as I had several browser instances in dynamically opening tabs and browsers were not opening fast enough and throwing errors.

So the second option is to set up a Loaded event using the interactivity namespace like this:

<wpf:ChromiumWebBrowser Address="{Binding Html, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" x:Name="ChromiumWebBrowser">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding BrowserLoadedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</wpf:ChromiumWebBrowser>

After this you can get the browser in your view model (or do this in a manager class), and set its RequestHandler to your custom BrowserRequestHandler like this:

browser.RequestHandler = new BrowserRequestHandler();
Olav answered 15/10, 2017 at 11:12 Comment(1)
You can skip writing all but the OnBeforeBrowse() {...} method by deriving BrowserRequestHandler from DefaultRequestHandler. It will supply all the boilerplateAnnabelle
E
3

It seems like it possible through use of the OnBeforeNavigation or OnBeforeBrowse events. See following references from "CEF Forum":

A suggested implementation of the OnBeforeNavigation method (from Sending information from Chromium Embeded (Javascript) to a containing C++ application):

    //declare (i.e. in header) 
    virtual bool OnBeforeNavigation(CefRefPtr<CefBrowser> browser, 
        CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, 
        NavigationType navigation_type, bool is_redirect)  OVERRIDE; 

    //implementation 
    bool CClientApp::OnBeforeNavigation(CefRefPtr<CefBrowser> browser, 
        CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, 
        NavigationType navigation_type, bool is_redirect)
    {
        CefString cefval = request->GetURL(); 
        CString csval = cefval.c_str(); 

        if ( /* Match csval against your url/link */ )
        {
            //process the command here 

            //this is a command and not really intended for navigation 
            return true; 
        }

        return false; //true cancels navigation, false allows it 
    }

Disclaimer: I haven't tried this code myself, but it should do the trick

Elijah answered 6/7, 2015 at 10:28 Comment(1)
As OnBeforeNavigation executes in the renderer process, it's not exposed by CefSharp which uses a separate executable for the sub processes. OnBeforeBrowse is however, rest of the implementation detail is basically the same. github.com/cefsharp/CefSharp/blob/cefsharp/41/CefSharp/…Leboeuf
B
2

I tried using a RequestHandler as suggested, however I found that while the link was opening correctly in a browser, the ChromiumWebBrowser was still opening an empty Chromium window. Instead of using the RequestHandler, I found that this could be more smoothly implemented using an ILifeSpanHandler - which allows me to catch the popup opening and redirect the process at that point.

Here is the code I used for the ILifeSpanHandler:

public class ChromiumLifeSpanHandler : ILifeSpanHandler
    {
        public bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
        {
            return false;
        }

        public void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser)
        {

        }

        public void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
        {

        }

        public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
        {
            newBrowser = null;
            System.Diagnostics.Process.Start(targetUrl);
            return true;
        }
    }

Here is how I implemented it in WPF (I just stuck it in a grid to have a place to show the static resource):

<Grid>
    <Grid.Resources>
        <local:ChromiumLifeSpanHandler x:Key="popupHandler"/>
    </Grid.Resources>
    <cef:ChromiumWebBrowser Address="{Binding TwitterFeedAddress}" 
                            LifeSpanHandler="{StaticResource popupHandler}"/>
</Grid>
Bufflehead answered 17/1, 2019 at 17:18 Comment(1)
Simple and beautiful, thank you!Idolatrous
C
0

Correcting a small typeo in Chaya answer (which worked for me also). x:Key="" is missing the L.

<Grid>
    <Grid.Resources>
        <local:ChromiumLifeSpanHandler x:Key="popupHandler"/>
    </Grid.Resources>
    <cef:ChromiumWebBrowser Address="{Binding TwitterFeedAddress}" 
                            LifeSpanHandler="{StaticResource popupHandler}"/>
</Grid>
Cattail answered 20/5, 2020 at 19:22 Comment(1)
I've fixed the typo. Rather than posting an answer with a correction you can propose an edit see stackoverflow.com/help/editingLeboeuf

© 2022 - 2024 — McMap. All rights reserved.