Running Local Web Pages in CefSharp.WPF

I needed to create a program which will run a web page from the local file system. Using the WebBrowser control wasn’t really an option since that uses the Trident engine which didn’t support many of the things we were trying to do in the web page. I went with the latest build of CefSharp.Wpf, but that doesn’t natively run local files, so it had to be modified. Fortunately, I found some posts on how to enable that feature (here and here) – however, the first link was for an older version of CefSharp, and the second link only works with static resource files. I ended up merging both concepts to get it to work.

The code below works on version 53.0.1 of CefSharp.Wpf. 

First, the following two classes needed to be created:

LocalResourceHandlerFactory.cs:

using System;
using CefSharp;
namespace MyCustomNamespace
{
    public class LocalResourceHandlerFactory : IResourceHandlerFactory
    {
        public bool HasHandlers
        {
            get
            {
                return true;
            }
        }

        public IResourceHandler GetResourceHandler(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request)
        {
            return new LocalResourceHandler();
        }
    }
}

LocalResourceHandler.cs:

using System;
using System.IO;
using CefSharp;
using System.Collections.Generic;

namespace MyCustomNamespace
{
    class LocalResourceHandler : IResourceHandler
    {
        private string mimeType;
        private MemoryStream stream;

        public void Cancel()
        {

        }

        public bool CanGetCookie(Cookie cookie)
        {
            return true;
        }

        public bool CanSetCookie(Cookie cookie)
        {
            return true;
        }

        public void Dispose()
        {

        }

        public void GetResponseHeaders(IResponse response, out long responseLength, out string redirectUrl)
        {
            responseLength = stream == null ? 0 : stream.Length;
            redirectUrl = null;

            response.StatusCode = (int)System.Net.HttpStatusCode.OK;
            response.StatusText = "OK";
            response.MimeType = mimeType;
        }

        public bool ProcessRequest(IRequest request, ICallback callback)
        {
            // The 'host' portion is entirely ignored by this scheme handler.
            var uri = new Uri(request.Url);
            var file = uri.AbsolutePath;

            if (File.Exists(file))
            {
                Byte[] bytes = File.ReadAllBytes(file);
                stream = new MemoryStream(bytes);
                switch (Path.GetExtension(file))
                {
                    case ".html":
                    case ".htm":
                        mimeType = "text/html";
                        break;
                    case ".css":
                        mimeType = "text/css";
                        break;
                    case ".js":
                        mimeType = "text/javascript";
                        break;
                    case ".png":
                        mimeType = "image/png";
                        break;
                    case ".jpg":
                    case ".jpeg":
                        mimeType = "image/jpeg";
                        break;
                    case ".gif":
                        mimeType = "image/gif";
                        break;
                    case ".woff":
                        mimeType = "application/font-woff";
                        break;
                    case ".eot":
                        mimeType = "application/vnd.ms-fontobject";
                        break;
                    case ".ttf":
                        mimeType = "application/font-ttf";
                        break;
                    case ".otf":
                        mimeType = "application/font-otf";
                        break;
                    case ".svg":
                        mimeType = "image/svg+xml";
                        break;
                    case ".appcache":
                    case ".manifest":
                        mimeType = "text/cache-manifest";
                        break;
                    default:
                        mimeType = "application/octet-stream";
                        break;
                }
                callback.Continue();
                return true;
            }
            return false;
        }

        public bool ReadResponse(Stream dataOut, out int bytesRead, ICallback callback)
        {
            //Dispose the callback as it's an unmanaged resource, we don't need it in this case
            callback.Dispose();

            if (stream == null)
            {
                bytesRead = 0;
                return false;
            }

            //Data out represents an underlying buffer (typically 32kb in size).
            var buffer = new byte[dataOut.Length];
            bytesRead = stream.Read(buffer, 0, buffer.Length);

            dataOut.Write(buffer, 0, buffer.Length);

            return bytesRead > 0;
        }
    }
}

With the above code in place, we can now load the web page from a local file. The code below assumes that the HTML files are located in a ‘web’ subdirectory (for example, /bin/debug/web/)

public MainWindow()
        {
            
            var settings = new CefSharp.CefSettings { RemoteDebuggingPort = 1234 };
            // for some reason, performance sucks w/ the gpu enabled
            settings.CefCommandLineArgs.Add("disable-gpu", "1");
            settings.CefCommandLineArgs.Add("disable-gpu-vsync", "1");
            CefSharp.Cef.Initialize(settings);

            InitializeComponent();

            browser.ResourceHandlerFactory = new LocalResourceHandlerFactory();

            browser.BrowserSettings.FileAccessFromFileUrls = CefSharp.CefState.Enabled;
            browser.BrowserSettings.UniversalAccessFromFileUrls = CefSharp.CefState.Enabled;
            browser.BrowserSettings.WebSecurity = CefSharp.CefState.Disabled;
            //browser.BrowserSettings.WebGl = CefSharp.CefState.Disabled;



            string dir = System.IO.Directory.GetCurrentDirectory();
            String page = string.Format("{0}/web/index.html", dir);
            browser.Address = page;
        }

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.