Click here to Skip to main content
15,867,453 members
Articles / Web Development / HTML

Chromium (CefSharp) Tor Browser

Rate me:
Please Sign up or sign in to vote.
5.00/5 (11 votes)
28 Jan 2016CPOL5 min read 95.8K   6.8K   21   42
An alternative Tor Browser built with C# using CefSharp and Tor.NET.

Introduction

My favorite browser is Google Chrome. It's fast and easy to use.

When I hit a web site which I can not access because of a web filter, one of my options is to use the original Tor Browser which is a custom version of Firefox and I can simply say that "I don't like it".

When I read the excellent article "Tor.NET - A managed Tor network library" by Chris Copeland, I started to play with the sample application in the project which uses the Internet Explorer component (WebBrowser). I've been using CefSharp Chromium browser component in my projects for some time and I changed the WebBrowser in the project with CefSharp and this is how this application came to life.

Ingredients

This application uses CefSharp as the web browser and the Tor.NET library to connect to the Tor network via an HTTP proxy. Please see the references section for detailed information about these two components.

Application

This is how the application looks when you start it.

Main screen of the application

It is a standard tabbed web browser which mimics Google Chrome.

When the application starts, it connects to the Tor network and starts the HTTP proxy. When Tor is ready to use, the address bar goes pale green.

"Downloads" button opens the downloads page.

"DuckDuckGo" button opens the alternative DuckDuckGo search engine with a ".onion" Tor address.

You can check if you are really connected to the Tor network by clicking the "Check if Tor is active" button.

Interesting Parts of the Code

The application is a WinForms application with only one form. However there are some interesting parts in the code which may be helpful for curious coders.

The following is the CefSharp initialization code.

C#
CefSettings settings = new CefSettings();

//Set proxy for Tor
settings.CefCommandLineArgs.Add("proxy-server", "127.0.0.1:8182");

//Load pepper flash player
//settings.CefCommandLineArgs.Add("ppapi-flash-path", appPath + @"PepperFlash\pepflashplayer.dll");

settings.RegisterScheme(new CefCustomScheme
{
    SchemeName = SchemeHandlerFactory.SchemeName,
    SchemeHandlerFactory = new SchemeHandlerFactory()
});

We make the CefSharp component to use our Tor proxy by adding the "proxy-server" command line argument.

If you want to use Flash in your browser, you can copy the PepperFlash folder from your Chrome installation to the application directory and uncomment the PepperFlash related line. Chrome is generally located in a folder like the following one:

C:\Program Files (x86)\Google\Chrome\Application\47.0.2526.111

But keep in mind that, if you are really concerned about your privacy, you are not recommended to use Flash when using Tor because Flash plugins might reach the Internet bypassing your proxy.

Tor is initialized by calling the InitializeTor function which is an exact copy from the Tor.NET sample application.

Handlers are used to make CefSharp to "behave". Here is a list of the CefSharp handlers and what they do:

DownloadHandler: Tells the application when a new download starts and about the progress of the ongoing downloads.

KeyboardHandler: When the focus is on the CefSharp browser, it "eats" all the keys pressed by the user. The application uses Ctrl-F4 to close the active browser tab so this handler helps to inform the application that Ctrl-F4 was pressed in the browser.

LifeSpanHandler: CefSharp calls the OnBeforePopup function of this handler before it opens a pop-up window and it tells CefSharp to open it in a new tab and not in a seperate window.

MenuHandler: I added the items from the Chrome Browser which I use most to the browser context menu. This handler does that stuff. An interesting extra feature of this context menu is the "Save as Pdf" option which does not exist in Google Chrome.

SchemeHandler: This handler helps to load the web pages that start with "chrome://".

"Downloads" Page

The "Downloads" Page

Google Chrome uses HTML interface for Settings, Downloads, Extensions and other pages so I did the same for the "Downloads" page.

The "Downloads" page of the application is a "clone" from the Chrome Browser which is an HTML page stored in the "storage" folder. To be able to load this page in the application, I defined a SchemeHandler and named it "chrome:". So when you type "chrome://storage/downloads.htm" in the address bar, CefSharp asks the SchemeHandler how to handle it and the handler returns the requested file from the storage folder.

Here is the JavaScript code from the downloads.htm page.

JavaScript
<script type="text/javascript">

    var $container;
    var $template;
    var timer;

    $(document).ready(function () {
        $container = $("#downloads-display");
        $template = $("#template");
        UpdateList();
        timer = setInterval(UpdateList, 500);
    });

    //...

    function UpdateItem(item) {
        var $item;
        var id = "d" + item.Id;
        $item = $("#" + id);
        //Add item if it does not exist
        if ($item.length == 0) {
            $item = $($template[0].outerHTML);
            $container.prepend($item);
            $item.removeAttr("hidden");
            $item.attr("id", id);
            $item.find("a.cancel").click(function () {
                host.cancelDownload(item.Id);
            });
            $item.find("img.icon").attr("src", "chrome://fileicon/" + item.SuggestedFileName);
            var startTime = getDate(item.StartTime);
            $item.find("div.since").text(startTime.format("dd.MM.yyyy"));
            $item.find("div.date").text(startTime.format("hh:mm:ss"));
            if (item.SuggestedFileName != "") $item.find("span.name").text(item.SuggestedFileName);
            $item.find("a.src-url").attr("href", item.Url).text(item.Url);
            $item.find("a.cancel").removeAttr("hidden");
        }
        var progress = "";
        if (item.IsInProgress) {
            progress = formatBytes(item.CurrentSpeed) + "/s - " + formatBytes(item.ReceivedBytes, 2);
            if (item.TotalBytes > 0) progress += " of " + formatBytes(item.TotalBytes, 2);
            if (item.PercentComplete > 0) progress += " (" + item.PercentComplete + "%)";
        } else {
            if (item.IsComplete) progress = "Complete";
            else if (item.IsCancelled) progress = "Cancelled";
            $item.find("a.cancel").attr("hidden","");
        }
        $item.find("span.status").text(progress);
    }

    function UpdateList() {
        host.getDownloads().then(function (res) {
            var list = JSON.parse(res);
            $.each(list, function (key, item) {
                UpdateItem(item);
            });
        });
    }

</script>

When the page loads, it sets a timer to update the downloads list every 500 miliseconds. 

JavaScript
timer = setInterval(UpdateList, 500);

The update function requests the list of download items from the main application and populates the list using this information.

JavaScript
function UpdateList() {
    host.getDownloads().then(function (res) {
        var list = JSON.parse(res);
        $.each(list, function (key, item) {
            UpdateItem(item);
        });
    });
}

With CefSharp, you can call .NET functions of your application from JavaScript.

The following is the line of code which binds a .NET class to the browser object:

C#
            if (url.StartsWith("chrome:"))
            {
                browser.RegisterAsyncJsObject("host", host, true);
            }

For security reasons, only pages loaded from inside the application are given a "host" object.

Functions on your "host" object can return only primitive types like String, Integer, Float etc. so I used a JSON Serializer (see References) to return a JSON string for complex objects.

C#
public string getDownloads()
{
    lock(myForm.downloads)
    {
        string x = JsonSerializer.SerializeToString(myForm.downloads);
        return x;
    }
}

The calling function in JavaScript uses JSON.parse to access this object:

JavaScript
function UpdateList() {
    host.getDownloads().then(function (res) {
        var list = JSON.parse(res);
        $.each(list, function (key, item) {
            UpdateItem(item);
        });
    });
}

Most Challenging Part

Google Chrome displays the icons of the downloaded files in the Downloads page.

Google Chrome downloads page

To able to do the same in my application, I added an extra case in the SchemeHandler:

C#
if (uri.Host == "fileicon")
{
    Task.Factory.StartNew(() =>
    {
        using (callback)
        {
            stream = GetFileIcon(fileName, IconReader.IconSize.Large);
            mimeType = ResourceHandler.GetMimeType(".png");
            callback.Continue();
        }
    });
    return true;
}

If you type "chrome://fileicon/somefile.zip" to the address bar, this code gets executed and it asks the operating system to return the icon for the given file type which it returns to the CefSharp browser.

Tab Component

I googled a lot but could not find a "free" tab component which looks like Google Chrome's on the Internet. If you know one, please provide a link in the comments.

References

History

  • Version 1.0.0 - Initial version of the application and the article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Turkey Turkey
I've been working with computers since the glorious times of Commodore 64. My latest beloved programming languages are C# and JavaScript.

Comments and Discussions

 
GeneralRe: TOR client Pin
Member 1258453525-Jul-16 1:26
Member 1258453525-Jul-16 1:26 
Questionwhy closing tab is so long? Pin
pige24-May-16 4:03
pige24-May-16 4:03 
AnswerRe: why closing tab is so long? Pin
ahmet_uzun24-May-16 4:15
ahmet_uzun24-May-16 4:15 
QuestionThis doesnt Open after being Complied Pin
User 1189479210-May-16 13:08
User 1189479210-May-16 13:08 
AnswerRe: This doesnt Open after being Complied Pin
ahmet_uzun10-May-16 23:00
ahmet_uzun10-May-16 23:00 
AnswerRe: This doesnt Open after being Complied Pin
KEL325-Jun-16 8:53
KEL325-Jun-16 8:53 
QuestionHave you consider to post this as a tip? Pin
Nelek28-Jan-16 22:21
protectorNelek28-Jan-16 22:21 
AnswerRe: Have you consider to post this as a tip? Pin
ahmet_uzun28-Jan-16 22:25
ahmet_uzun28-Jan-16 22:25 
GeneralRe: Have you consider to post this as a tip? Pin
Nelek28-Jan-16 22:47
protectorNelek28-Jan-16 22:47 
GeneralRe: Have you consider to post this as a tip? Pin
ahmet_uzun28-Jan-16 23:06
ahmet_uzun28-Jan-16 23:06 
GeneralRe: Have you consider to post this as a tip? Pin
Nelek29-Jan-16 0:12
protectorNelek29-Jan-16 0:12 
NewsFree tab control without reload child controls Pin
midnait28-Jan-16 19:12
midnait28-Jan-16 19:12 
GeneralRe: Free tab control without reload child controls Pin
ahmet_uzun28-Jan-16 21:12
ahmet_uzun28-Jan-16 21:12 
GeneralRe: Free tab control without reload child controls Pin
midnait31-Jan-16 5:23
midnait31-Jan-16 5:23 
GeneralRe: Free tab control without reload child controls Pin
ahmet_uzun1-Feb-16 22:17
ahmet_uzun1-Feb-16 22:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.