diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..1ff0c423042b46cb1d617b81efb715defbe8054d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs     diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following 
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln       merge=binary
+#*.csproj    merge=binary
+#*.vbproj    merge=binary
+#*.vcxproj   merge=binary
+#*.vcproj    merge=binary
+#*.dbproj    merge=binary
+#*.fsproj    merge=binary
+#*.lsproj    merge=binary
+#*.wixproj   merge=binary
+#*.modelproj merge=binary
+#*.sqlproj   merge=binary
+#*.wwaproj   merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg   binary
+#*.png   binary
+#*.gif   binary
+
+###############################################################################
+# diff behavior for common document formats
+# 
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the 
+# entries below.
+###############################################################################
+#*.doc   diff=astextplain
+#*.DOC   diff=astextplain
+#*.docx  diff=astextplain
+#*.DOCX  diff=astextplain
+#*.dot   diff=astextplain
+#*.DOT   diff=astextplain
+#*.pdf   diff=astextplain
+#*.PDF   diff=astextplain
+#*.rtf   diff=astextplain
+#*.RTF   diff=astextplain
diff --git a/AutoWebPerf/App.config b/AutoWebPerf/App.config
new file mode 100644
index 0000000000000000000000000000000000000000..8e15646352ec1d9a84bbc6504ef6b46e16bf7823
--- /dev/null
+++ b/AutoWebPerf/App.config
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
+    </startup>
+</configuration>
\ No newline at end of file
diff --git a/AutoWebPerf/AutoWebPerf.csproj b/AutoWebPerf/AutoWebPerf.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..44b83369ebd73ba70f6d35c3022754d4fb42ba96
--- /dev/null
+++ b/AutoWebPerf/AutoWebPerf.csproj
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{36138327-0A72-44E3-B9DB-C4E6155AAFD5}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>AutoWebPerf</RootNamespace>
+    <AssemblyName>AutoWebPerf</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Newtonsoft.Json">
+      <HintPath>packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+    <Reference Include="WebSocket4Net">
+      <HintPath>packages\WebSocket4Net.0.12\lib\net45\WebSocket4Net.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ChromeWrapper.cs" />
+    <Compile Include="ChromeProcessInfo.cs" />
+    <Compile Include="Chrome\IChromeProxy.cs" />
+    <Compile Include="Chrome\MessageContractResolver.cs" />
+    <Compile Include="Chrome\Messages\ChromeProxy.cs" />
+    <Compile Include="Chrome\Messages\ErrorResponse.cs" />
+    <Compile Include="Chrome\Messages\IdResponse.cs" />
+    <Compile Include="Chrome\Messages\IError.cs" />
+    <Compile Include="Chrome\Messages\IRequest.cs" />
+    <Compile Include="Chrome\Messages\IRequestFactory.cs" />
+    <Compile Include="Chrome\Messages\IResponse.cs" />
+    <Compile Include="Chrome\Messages\IResponseFactory.cs" />
+    <Compile Include="Chrome\Messages\MethodName.cs" />
+    <Compile Include="Chrome\Messages\MethodNameAttribute.cs" />
+    <Compile Include="Chrome\Messages\Network\DataReceivedResponse.cs" />
+    <Compile Include="Chrome\Messages\Network\EnableRequest.cs" />
+    <Compile Include="Chrome\Messages\Network\EnableResponse.cs" />
+    <Compile Include="Chrome\Messages\Network\LoadingFinishedResponse.cs" />
+    <Compile Include="Chrome\Messages\Network\RequestServedFromCacheResponse.cs" />
+    <Compile Include="Chrome\Messages\Network\RequestWillBeSentResponse.cs" />
+    <Compile Include="Chrome\Messages\Network\ResponseReceivedResponse.cs" />
+    <Compile Include="Chrome\Messages\Page\NavigateRequest.cs" />
+    <Compile Include="Chrome\Messages\Page\NavigateRequestParams.cs" />
+    <Compile Include="Chrome\Messages\Page\NavigateResponse.cs" />
+    <Compile Include="Chrome\Messages\Page\NavigateResponseResult.cs" />
+    <Compile Include="Chrome\Messages\Request.cs" />
+    <Compile Include="Chrome\Messages\RequestFactory.cs" />
+    <Compile Include="Chrome\Messages\ResponseFactory.cs" />
+    <Compile Include="Chrome\Messages\ResponseHandler.cs" />
+    <Compile Include="Chrome\Messages\Response.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="RemoveSessions.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/AutoWebPerf/AutoWebPerf.sln b/AutoWebPerf/AutoWebPerf.sln
new file mode 100644
index 0000000000000000000000000000000000000000..423af3a79dbd27184d524ce889479f5bfb8fb0d4
--- /dev/null
+++ b/AutoWebPerf/AutoWebPerf.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoWebPerf", "AutoWebPerf.csproj", "{36138327-0A72-44E3-B9DB-C4E6155AAFD5}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{36138327-0A72-44E3-B9DB-C4E6155AAFD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{36138327-0A72-44E3-B9DB-C4E6155AAFD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{36138327-0A72-44E3-B9DB-C4E6155AAFD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{36138327-0A72-44E3-B9DB-C4E6155AAFD5}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/AutoWebPerf/Chrome/IChromeProxy.cs b/AutoWebPerf/Chrome/IChromeProxy.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9052918e17db36984c5de4146edbf6289be0db41
--- /dev/null
+++ b/AutoWebPerf/Chrome/IChromeProxy.cs
@@ -0,0 +1,18 @@
+using AutoWebPerf.Chrome.Messages;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome
+{
+    interface IChromeProxy : IDisposable
+    {
+        Task Init();
+
+        Task<IResponse> PublishAsync(IRequest request);
+
+        void Subscribe<T>(ResponseHandler handler);
+    }
+}
diff --git a/AutoWebPerf/Chrome/MessageContractResolver.cs b/AutoWebPerf/Chrome/MessageContractResolver.cs
new file mode 100644
index 0000000000000000000000000000000000000000..df78bd57f27aedddd186f86f0e226ea825998361
--- /dev/null
+++ b/AutoWebPerf/Chrome/MessageContractResolver.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json.Serialization;
+
+namespace AutoWebPerf.Chrome
+{
+    class MessageContractResolver : DefaultContractResolver
+    {
+        protected override string ResolvePropertyName(string propertyName)
+        {
+            return propertyName.ToLower();
+        }
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/ChromeProxy.cs b/AutoWebPerf/Chrome/Messages/ChromeProxy.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7c05a9ed69ae74fb953fed33bdb83190489a47a8
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/ChromeProxy.cs
@@ -0,0 +1,138 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using WebSocket4Net;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    class ChromeProxy : IChromeProxy
+    {
+        private ManualResetEvent _openEvent = new ManualResetEvent(false);
+        private ManualResetEvent _publishEvent = new ManualResetEvent(false);
+        private readonly string _endpoint;
+        private WebSocket _webSocket;
+        private readonly ConcurrentDictionary<string, ConcurrentBag<ResponseHandler>> _handlers = new ConcurrentDictionary<string, ConcurrentBag<ResponseHandler>>();
+        private IResponseFactory _responseFactory;
+        private ConcurrentDictionary<long, ManualResetEvent> _requestWaitHandles = new ConcurrentDictionary<long, ManualResetEvent>();
+        private ConcurrentDictionary<long, IResponse> _responses = new ConcurrentDictionary<long, IResponse>();
+
+        public ChromeProxy(string endpoint, IResponseFactory responseFactory)
+        {
+            _endpoint = endpoint;
+            _responseFactory = responseFactory;
+        }
+
+        public Task Init()
+        {
+            _openEvent.Reset();
+            _webSocket = new WebSocket(_endpoint);
+            _webSocket.Opened += delegate(System.Object o, EventArgs e)
+            {
+                _openEvent.Set();
+            };
+
+            _webSocket.MessageReceived += delegate(System.Object o, MessageReceivedEventArgs e)
+            {
+                var response = _responseFactory.Create(e.Message);
+                var idResponse = response as IdResponse;
+                ManualResetEvent requestMre = null;
+                long responseId;
+                if (null != idResponse 
+                    && long.TryParse(idResponse.Id, out responseId)
+                    && _requestWaitHandles.TryGetValue(responseId, out requestMre))
+                {
+                    _responses.AddOrUpdate(responseId, response, (i, r) => response);
+                    requestMre.Set();
+                }
+                else
+                {
+                    HandleResponse(response);
+                }
+            };
+
+            _webSocket.Error += delegate(System.Object o, SuperSocket.ClientEngine.ErrorEventArgs e)
+            {
+                throw e.Exception;
+            };
+
+            _webSocket.Closed += delegate(System.Object o, EventArgs e)
+            {
+            };
+
+            _webSocket.DataReceived += delegate(System.Object o, WebSocket4Net.DataReceivedEventArgs e)
+            {
+                var response = _responseFactory.Create(e.Data);
+                HandleResponse(response);
+            };
+
+            _webSocket.Open();
+            return Task.Run(() =>
+            {
+                _openEvent.WaitOne();
+            });
+        }
+
+        private void HandleResponse(IResponse response)
+        {
+            var type = response.GetType();
+            var handlerKey = type.FullName;
+            ConcurrentBag<ResponseHandler> handlers = null;
+            if(_handlers.TryGetValue(handlerKey, out handlers))
+            {
+                var localHandlers = handlers.ToArray();
+                foreach(var handler in localHandlers)
+                {
+                    handler(this, response);
+                }
+            }
+        }
+
+        public Task<IResponse> PublishAsync(IRequest request)
+        {
+            var settings = new JsonSerializerSettings 
+            {
+                ContractResolver = new MessageContractResolver()
+            };
+            var requestString = JsonConvert.SerializeObject(request, settings);
+
+            var requestResetEvent = new ManualResetEvent(false);
+            _requestWaitHandles.AddOrUpdate(request.Id, requestResetEvent, (id, r) => requestResetEvent);
+            return Task.Run(() =>
+                {
+                    _webSocket.Send(requestString);
+                    requestResetEvent.WaitOne();
+                    IResponse response = null;
+                    _requestWaitHandles.TryRemove(request.Id, out requestResetEvent);
+                    _responses.TryRemove(request.Id, out response);
+                    return response;
+                });
+        }
+
+        public void Subscribe<T>(ResponseHandler handler)
+        {
+            var handlerType = typeof(T);
+            _handlers.AddOrUpdate(handlerType.FullName, 
+                (m) => new ConcurrentBag<ResponseHandler>(new[] { handler }),
+                (m, currentBag) => 
+                {
+                    currentBag.Add(handler);
+                    return currentBag;
+                });
+        }
+
+        public void Dispose()
+        {
+            if (null == _webSocket) return;
+            if (_webSocket.State == WebSocketState.Open)
+            {
+                _webSocket.Close();
+            }
+            _webSocket.Dispose();
+        }
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/ErrorResponse.cs b/AutoWebPerf/Chrome/Messages/ErrorResponse.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ee121e592a5d269e3ec85e023888cf1b53c35e43
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/ErrorResponse.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    public class Error
+    {
+        public int Code { get; set; }
+        public string message { get; set; }
+    }
+
+    class ErrorResponse : IdResponse
+    {
+        public Error Error { get; set; }
+        public int Id { get; set; }
+    }
+
+
+
+
+}
diff --git a/AutoWebPerf/Chrome/Messages/IError.cs b/AutoWebPerf/Chrome/Messages/IError.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2d91b4cfc6a0d1e04c1fd5f8f0bdbc6fd12b72df
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/IError.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    interface IError
+    {
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/IRequest.cs b/AutoWebPerf/Chrome/Messages/IRequest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e3620f422e78cbdb4c1dc73abefd4eef1cad09f4
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/IRequest.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    interface IRequest
+    {
+        long Id { get; }
+
+        string Method { get; }
+    }
+
+    interface IRequest<T> : IRequest
+    {
+        T Params { get; }
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/IRequestFactory.cs b/AutoWebPerf/Chrome/Messages/IRequestFactory.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9d621ea521b8ff9e92d2f4fe840879f04c3a0d23
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/IRequestFactory.cs
@@ -0,0 +1,19 @@
+using AutoWebPerf.Chrome.Messages.Page;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    interface IRequestFactory
+    {
+        string GetMethod(long id);
+
+        
+        NavigateRequest CreateNavigateRequest(string url);
+
+        Network.EnableRequest CreateNetworkEnableRequest();
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/IResponse.cs b/AutoWebPerf/Chrome/Messages/IResponse.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2890de12c7e02ff4f07878bbec24ea43e190f5f6
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/IResponse.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    interface IResponse
+    {
+    }
+
+    interface IResponse<T> : IResponse
+    {
+        T Result { get; }
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/IResponseFactory.cs b/AutoWebPerf/Chrome/Messages/IResponseFactory.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0fa26d26e99b9d13f42042f9ff03dfaee04942f7
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/IResponseFactory.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    interface IResponseFactory
+    {
+        IResponse Create(string responseText);
+
+        IResponse Create(byte[] responseBytes);
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/IdResponse.cs b/AutoWebPerf/Chrome/Messages/IdResponse.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a42f309258a5c054ff141d542c2102bda2a1779e
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/IdResponse.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    class IdResponse : Response
+    {
+        public string Id { get; set; }
+    }
+
+    class IdResponse<T> : IdResponse, IResponse<T>
+    {
+        public T Result
+        {
+            get;
+            set;
+        }
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/MethodName.cs b/AutoWebPerf/Chrome/Messages/MethodName.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8de1908031a4ceb87126412536ee62a6b310f7af
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/MethodName.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    public static class MethodName
+    {
+        public static class Network
+        {
+            public const string DataReceived = "Network.dataReceived";
+            public const string Enable = "Network.enable";
+            public const string LoadingFinished = "Network.loadingFinished";
+            public const string RequestWillBeSent = "Network.requestWillBeSent";
+            public const string ResponseReceived = "Network.responseReceived";
+            public const string RequestServedFromCache = "Network.requestServedFromCache";
+        }
+        public static class Page
+        {
+            public const string Navigate = "Page.navigate";
+        }
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/MethodNameAttribute.cs b/AutoWebPerf/Chrome/Messages/MethodNameAttribute.cs
new file mode 100644
index 0000000000000000000000000000000000000000..21688b3151da5a007726fc63912833bcbe188460
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/MethodNameAttribute.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    [AttributeUsage(AttributeTargets.Class, Inherited = false)]
+    public class MethodNameAttribute : Attribute
+    {
+        public MethodNameAttribute(string methodName)
+        {
+            this.MethodName = methodName;
+        }
+
+        public string MethodName { get; private set; }
+    }
+}
\ No newline at end of file
diff --git a/AutoWebPerf/Chrome/Messages/Network/DataReceivedResponse.cs b/AutoWebPerf/Chrome/Messages/Network/DataReceivedResponse.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0be64d4674612c55b1169ac937970e1ef50477a1
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Network/DataReceivedResponse.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages.Network
+{
+    [MethodName(MethodName.Network.DataReceived)]
+    public class DataReceivedResponse : IResponse
+    {
+        public string method { get; set; }
+        public DataReceivedResponseParams Params { get; set; }
+    }
+
+    public class DataReceivedResponseParams
+    {
+        public string requestId { get; set; }
+        public float timestamp { get; set; }
+        public int dataLength { get; set; }
+        public int encodedDataLength { get; set; }
+    }
+
+}
diff --git a/AutoWebPerf/Chrome/Messages/Network/EnableRequest.cs b/AutoWebPerf/Chrome/Messages/Network/EnableRequest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7f19a27ab1b0514d2bf0d8fd7de7f0800c03f073
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Network/EnableRequest.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages.Network
+{
+    [MethodName(MethodName.Network.Enable)]
+    public class EnableRequest : Messages.Request
+    {
+        public EnableRequest(long id)
+            : base(id)
+        {                
+        }
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/Network/EnableResponse.cs b/AutoWebPerf/Chrome/Messages/Network/EnableResponse.cs
new file mode 100644
index 0000000000000000000000000000000000000000..796ae0e43b586fd8c6500c74c7cc2c130374a404
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Network/EnableResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages.Network
+{
+    [MethodName(MethodName.Network.Enable)]
+    class EnableResponse : Messages.IdResponse
+    {
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/Network/LoadingFinishedResponse.cs b/AutoWebPerf/Chrome/Messages/Network/LoadingFinishedResponse.cs
new file mode 100644
index 0000000000000000000000000000000000000000..46f1f08975e3515d96cc93d6b9d34a862fd08d7d
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Network/LoadingFinishedResponse.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages.Network
+{
+
+    [MethodName(MethodName.Network.LoadingFinished)]
+    public class LoadingFinishedResponse : IResponse
+    {
+        public string method { get; set; }
+        public LoadingFinishedResponseParams Params { get; set; }
+    }
+
+    public class LoadingFinishedResponseParams
+    {
+        public string requestId { get; set; }
+        public float timestamp { get; set; }
+        public int encodedDataLength { get; set; }
+    }
+
+}
diff --git a/AutoWebPerf/Chrome/Messages/Network/RequestServedFromCacheResponse.cs b/AutoWebPerf/Chrome/Messages/Network/RequestServedFromCacheResponse.cs
new file mode 100644
index 0000000000000000000000000000000000000000..190c04c89a2ffcf4ac8af802d6bb0c230a9f6e08
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Network/RequestServedFromCacheResponse.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages.Network
+{
+    [MethodName(MethodName.Network.RequestServedFromCache)]
+    public class RequestServedFromCacheResponse : IResponse
+    {
+        public string method { get; set; }
+        public RequestServedFromCacheResponseParams Params { get; set; }
+    }
+
+    public class RequestServedFromCacheResponseParams
+    {
+        public string requestId { get; set; }
+    }
+
+}
diff --git a/AutoWebPerf/Chrome/Messages/Network/RequestWillBeSentResponse.cs b/AutoWebPerf/Chrome/Messages/Network/RequestWillBeSentResponse.cs
new file mode 100644
index 0000000000000000000000000000000000000000..df0f237fecc2919ef40a34b81df9f3607d13bcba
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Network/RequestWillBeSentResponse.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages.Network
+{
+    [MethodName(MethodName.Network.RequestWillBeSent)]
+    public class RequestWillBeSentResponse : IResponse
+    {
+        public string method { get; set; }
+        public RequestWillBeSentResponseParams Params { get; set; }
+    }
+
+    public class RequestWillBeSentResponseParams
+    {
+        public string requestId { get; set; }
+        public string frameId { get; set; }
+        public string loaderId { get; set; }
+        public string documentURL { get; set; }
+        public Request request { get; set; }
+        public float timestamp { get; set; }
+        public Initiator initiator { get; set; }
+        public string type { get; set; }
+    }
+
+    public class Request
+    {
+        public string url { get; set; }
+        public string method { get; set; }
+        public RequestHeaders headers { get; set; }
+    }
+
+    public class RequestHeaders
+    {
+        public string Accept { get; set; }
+        public string UserAgent { get; set; }
+    }
+
+    public class Initiator
+    {
+        public string type { get; set; }
+    }
+
+}
diff --git a/AutoWebPerf/Chrome/Messages/Network/ResponseReceivedResponse.cs b/AutoWebPerf/Chrome/Messages/Network/ResponseReceivedResponse.cs
new file mode 100644
index 0000000000000000000000000000000000000000..de27258a680f6696ae23051ba9f87cd10f0f7036
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Network/ResponseReceivedResponse.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages.Network
+{
+    [MethodName(MethodName.Network.ResponseReceived)]
+    public class ResponseReceivedResponse : IResponse
+    {
+        public string method { get; set; }
+        public ResponseReceivedResponseParams Params { get; set; }
+    }
+
+    public class ResponseReceivedResponseParams
+    {
+        public string requestId { get; set; }
+        public string frameId { get; set; }
+        public string loaderId { get; set; }
+        public float timestamp { get; set; }
+        public string type { get; set; }
+        public Response response { get; set; }
+    }
+
+    public class Response
+    {
+        public string url { get; set; }
+        public int status { get; set; }
+        public string statusText { get; set; }
+        public ResponseHeaders headers { get; set; }
+        public string mimeType { get; set; }
+        public bool connectionReused { get; set; }
+        public int connectionId { get; set; }
+        public int encodedDataLength { get; set; }
+        public bool fromDiskCache { get; set; }
+        public bool fromServiceWorker { get; set; }
+        public Timing timing { get; set; }
+        public string headersText { get; set; }
+        public Requestheaders requestHeaders { get; set; }
+        public string requestHeadersText { get; set; }
+        public string remoteIPAddress { get; set; }
+        public int remotePort { get; set; }
+        public string protocol { get; set; }
+    }
+
+    public class ResponseHeaders
+    {
+        public string Date { get; set; }
+        public string ContentEncoding { get; set; }
+        public string Server { get; set; }
+        public string XAspNetVersion { get; set; }
+        public string XPoweredBy { get; set; }
+        public string Vary { get; set; }
+        public string ContentType { get; set; }
+        public string accesscontrolalloworigin { get; set; }
+        public string CacheControl { get; set; }
+        public string SetCookie { get; set; }
+        public string accesscontrolallowheaders { get; set; }
+        public string ContentLength { get; set; }
+        public string Expires { get; set; }
+    }
+
+    public class Timing
+    {
+        public float requestTime { get; set; }
+        public int proxyStart { get; set; }
+        public int proxyEnd { get; set; }
+        public int dnsStart { get; set; }
+        public int dnsEnd { get; set; }
+        public int connectStart { get; set; }
+        public int connectEnd { get; set; }
+        public int sslStart { get; set; }
+        public int sslEnd { get; set; }
+        public int serviceWorkerFetchStart { get; set; }
+        public int serviceWorkerFetchReady { get; set; }
+        public int serviceWorkerFetchEnd { get; set; }
+        public float sendStart { get; set; }
+        public float sendEnd { get; set; }
+        public float receiveHeadersEnd { get; set; }
+    }
+
+    public class Requestheaders
+    {
+        public string Accept { get; set; }
+        public string Connection { get; set; }
+        public string AcceptEncoding { get; set; }
+        public string Host { get; set; }
+        public string AcceptLanguage { get; set; }
+        public string UserAgent { get; set; }
+    }
+
+}
diff --git a/AutoWebPerf/Chrome/Messages/Page/NavigateRequest.cs b/AutoWebPerf/Chrome/Messages/Page/NavigateRequest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bcdf05a55a4a06c72229c01b8b97f73dee056eaa
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Page/NavigateRequest.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages.Page
+{
+    [MethodName("Page.navigate")]
+    class NavigateRequest : Request<NavigateRequestParams>
+    {
+        public NavigateRequest(long id, NavigateRequestParams param)
+            : base(id, param)
+        {
+            
+        }
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/Page/NavigateRequestParams.cs b/AutoWebPerf/Chrome/Messages/Page/NavigateRequestParams.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2227e0fbea1c1d6df71a0ab6a17341bbcf37df51
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Page/NavigateRequestParams.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages.Page
+{
+    class NavigateRequestParams
+    {
+        public string Url { get; set; }
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/Page/NavigateResponse.cs b/AutoWebPerf/Chrome/Messages/Page/NavigateResponse.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6439585b90e5274f148ae703013df71cc3e25e4e
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Page/NavigateResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages.Page
+{
+    [MethodName(MethodName.Page.Navigate)]
+    class NavigateResponse : IdResponse<NavigateResponseResult>
+    {
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/Page/NavigateResponseResult.cs b/AutoWebPerf/Chrome/Messages/Page/NavigateResponseResult.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b0133b39305ca939d5043ef53fdfd7660be89470
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Page/NavigateResponseResult.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages.Page
+{
+    class NavigateResponseResult
+    {
+        public string FrameId { get; set; }
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/Request.cs b/AutoWebPerf/Chrome/Messages/Request.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f1816dee39e1b3400ab313a231e6c4b41ff86085
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Request.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    public class Request : IRequest
+    {
+        public Request(long id)
+        {
+            Id = id;
+            Method = LookupMethodAttribute();
+        }
+
+        private string LookupMethodAttribute()
+        {
+            var methodNameAttribute = this.GetType().GetCustomAttributes(typeof(MethodNameAttribute), true)
+                .FirstOrDefault() as MethodNameAttribute;
+            if(null == methodNameAttribute)
+            {
+                throw new Exception("Request(id) constructor is called on an object without a [MethodName] attribute");
+            }
+            return methodNameAttribute.MethodName;
+        }
+
+        public Request(long id, string method)
+        {
+            Id = id;
+            Method = method;
+        }
+
+        public long Id
+        {
+            get;
+            private set;
+        }
+
+        public string Method
+        {
+            get;
+            private set;
+        }
+    }
+
+    public class Request<T> : Request, IRequest<T>
+    {
+        public Request(long id, T methodParams)
+            : base(id)
+        {
+            Params = methodParams;
+        }
+        
+        public T Params
+        {
+            get;
+            private set;
+        }
+    }
+
+}
diff --git a/AutoWebPerf/Chrome/Messages/RequestFactory.cs b/AutoWebPerf/Chrome/Messages/RequestFactory.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fa33724e60b4671a9659e245ebcd3a908df7f2f5
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/RequestFactory.cs
@@ -0,0 +1,47 @@
+using AutoWebPerf.Chrome.Messages.Page;
+using System;
+using System.Collections.Concurrent;
+using System.Threading;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    internal class RequestFactory : IRequestFactory
+    {
+        private readonly ConcurrentDictionary<long, string> _methods = new ConcurrentDictionary<long, string>();
+        private long _count = 0;
+        public NavigateRequest CreateNavigateRequest(string url)
+        {
+            return CreateRequest(id =>
+                {
+                    var requestParams = new NavigateRequestParams
+                    {
+                        Url = url
+                    };
+                    return new NavigateRequest(id, requestParams);
+                });
+        }
+
+        public Network.EnableRequest CreateNetworkEnableRequest()
+        {
+            return CreateRequest(id => new Network.EnableRequest(id));
+        }
+
+        public string GetMethod(long id)
+        {
+            return _methods[id];
+        }
+
+        private T CreateRequest<T>(Func<long, T> factory) where T : IRequest
+        {
+            var requestId = GetNextRequestId();
+            var request = factory(requestId);
+            _methods.AddOrUpdate(request.Id, request.Method, (id, m) => request.Method);
+            return request;
+        }
+
+        private long GetNextRequestId()
+        {
+            return Interlocked.Increment(ref _count);
+        }
+    }
+}
\ No newline at end of file
diff --git a/AutoWebPerf/Chrome/Messages/Response.cs b/AutoWebPerf/Chrome/Messages/Response.cs
new file mode 100644
index 0000000000000000000000000000000000000000..eb1a2015982d34d53b969d503a2d644edd39e640
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/Response.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    class Response : IResponse
+    {
+    }
+    class Response<T> : IResponse<T>
+    {
+        public T Result
+        {
+            get;
+            set;
+        }
+    }
+
+}
diff --git a/AutoWebPerf/Chrome/Messages/ResponseFactory.cs b/AutoWebPerf/Chrome/Messages/ResponseFactory.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6543a7c944af89c78eedb06cac440e7ece832896
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/ResponseFactory.cs
@@ -0,0 +1,94 @@
+using AutoWebPerf.Chrome.Messages.Network;
+using AutoWebPerf.Chrome.Messages.Page;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    class ResponseFactory : IResponseFactory
+    {
+        private readonly static Lazy<Dictionary<string, Type>> _methodTypeMapLazy = new Lazy<Dictionary<string, Type>>(LoadMethodTypeMap);
+
+        private static Dictionary<string, Type> LoadMethodTypeMap()
+        {
+            var iresponseType = typeof(IResponse);
+            var assembly = Assembly.GetExecutingAssembly();
+            var assemblyTypes = assembly.GetTypes();
+            var responseTypes = assemblyTypes.Where(t => iresponseType.IsAssignableFrom(t) && t.IsClass);
+            Dictionary<string, Type> result = new Dictionary<string, Type>();
+            foreach(var responseType in responseTypes)
+            {
+                var methodNameAttribute = responseType.GetCustomAttribute<MethodNameAttribute>(false) as MethodNameAttribute;
+                if (null == methodNameAttribute) continue;
+                result[methodNameAttribute.MethodName] = responseType;
+            }
+            return result;
+        }
+
+        private readonly IRequestFactory _requestFactory;
+
+        public ResponseFactory(IRequestFactory requestFactory)
+        {
+            _requestFactory = requestFactory;
+        }
+
+        public IResponse Create(string responseText)
+        {
+            var jObject = JObject.Parse(responseText);
+            if(null != jObject["error"])
+            {
+                return jObject.ToObject<ErrorResponse>();
+            }
+            var response = ResponseFromMethodResponse(jObject);
+            if (null != response) return response;
+            response = ResponseFromMethodLookupResponse(jObject);
+            if (null != response) return response;
+            throw new Exception("We could not create a response from text: [" + responseText + "]");
+        }
+
+        private IResponse ResponseFromMethodLookupResponse(JObject jObject)
+        {
+            var resultObj = jObject["result"];
+            if (null == resultObj) return null;
+            var resultIdObj = jObject["id"];
+            if (null == resultIdObj) return null;
+            long resultId;
+            if (!long.TryParse(resultIdObj.ToString(), out resultId)) return null;
+            var methodLookupResult = _requestFactory.GetMethod(resultId);
+            if(String.IsNullOrEmpty(methodLookupResult)) return null;
+            return ResponseFromMethodResponse(methodLookupResult, jObject);
+        }
+
+        private IResponse ResponseFromMethodResponse(JObject jObject)
+        {
+            var method = jObject["method"];
+            if (null == method) return null;
+            return ResponseFromMethodResponse(method.ToString(), jObject);
+        }
+
+        private IResponse ResponseFromMethodResponse(string methodName, JObject jObject)
+        {
+            if(String.IsNullOrEmpty(methodName)) return null;
+            Type responseType = null;
+            if (!_methodTypeMapLazy.Value.TryGetValue(methodName, out responseType))
+            {
+                throw new ArgumentException("We could not find a response type for the method " + methodName);
+            }
+            var response = jObject.ToObject(responseType) as IResponse;
+            return response;
+        }
+
+
+        public IResponse Create(byte[] responseBytes)
+        {
+            var responseString = Encoding.Default.GetString(responseBytes);
+            return Create(responseString);
+        }
+    }
+}
diff --git a/AutoWebPerf/Chrome/Messages/ResponseHandler.cs b/AutoWebPerf/Chrome/Messages/ResponseHandler.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5142c08aeeec19f0485519c6a61e697b8d184a48
--- /dev/null
+++ b/AutoWebPerf/Chrome/Messages/ResponseHandler.cs
@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf.Chrome.Messages
+{
+    delegate void ResponseHandler(object sender, IResponse response);
+}
diff --git a/AutoWebPerf/ChromeProcessInfo.cs b/AutoWebPerf/ChromeProcessInfo.cs
new file mode 100644
index 0000000000000000000000000000000000000000..34cdf77f7455c630b50dba58444bb81a1589499c
--- /dev/null
+++ b/AutoWebPerf/ChromeProcessInfo.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf
+{
+    class ChromeProcessInfo
+    {
+        public DirectoryInfo ChromeUserDirectory { get; set; }
+        public Process ChromeProcess { get; set; }
+    }
+}
diff --git a/AutoWebPerf/ChromeWrapper.cs b/AutoWebPerf/ChromeWrapper.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4cf010df1569f1c4bf746d1fcc792aae7fb0e5cd
--- /dev/null
+++ b/AutoWebPerf/ChromeWrapper.cs
@@ -0,0 +1,155 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Runtime.Serialization.Json;
+using System.Text;
+using System.Threading.Tasks;
+using System.Net.WebSockets;
+using System.Threading;
+using WebSocket4Net;
+
+namespace AutoWebPerf
+{
+    /// <summary>
+    /// ABPS: https://markcz.wordpress.com/2012/02/18/automating-chrome-browser-from-csharp/
+    /// </summary>
+    public class ChromeWrapper
+    {
+        const string JsonPostfix = "/json";
+
+        string remoteDebuggingUri;
+        string sessionWSEndpoint;
+
+        public ChromeWrapper(string remoteDebuggingUri)
+        {
+            this.remoteDebuggingUri = remoteDebuggingUri;
+        }
+
+        public TRes SendRequest<TRes>()
+        {
+            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(remoteDebuggingUri + JsonPostfix);
+            var resp = req.GetResponse();
+            var respStream = resp.GetResponseStream();
+
+            StreamReader sr = new StreamReader(respStream);
+            var s = sr.ReadToEnd();
+            resp.Dispose();
+            return Deserialise<TRes>(s);
+        }
+
+        public List<RemoteSessionsResponse> GetAvailableSessions()
+        {
+            var res = this.SendRequest<List<RemoteSessionsResponse>>();
+            return (from r in res
+                    where r.devtoolsFrontendUrl != null
+                    select r).ToList();
+        }
+
+        public string NavigateTo(string uri)
+        {
+            // Page.navigate is working from M18
+            var json = @"{""method"":""Page.navigate"",""params"":{""url"":""" + uri + @"""},""id"":1}";
+
+            // Instead of Page.navigate, we can use document.location
+            //var json = @"{""method"":""Runtime.evaluate"",""params"":{""expression"":""document.location='" + uri + @"'"",""objectGroup"":""console"",""includeCommandLineAPI"":true,""doNotPauseOnExceptions"":false,""returnByValue"":false},""id"":1}";
+            return this.SendCommand(json);
+        }
+
+        public string GetElementsByTagName(string tagName)
+        {
+            // Page.navigate is working from M18
+            //var json = @"{""method"":""Page.navigate"",""params"":{""url"":""http://www.seznam.cz""},""id"":1}";
+
+            // Instead of Page.navigate, we can use document.location
+            var json = @"{""method"":""Runtime.evaluate"",""params"":{""expression"":""document.getElementsByTagName('" + tagName + @"')"",""objectGroup"":""console"",""includeCommandLineAPI"":true,""doNotPauseOnExceptions"":false,""returnByValue"":false},""id"":1}";
+            return this.SendCommand(json);
+        }
+
+
+        public string Eval(string cmd)
+        {
+            var json = @"{""method"":""Runtime.evaluate"",""params"":{""expression"":""" + cmd + @""",""objectGroup"":""console"",""includeCommandLineAPI"":true,""doNotPauseOnExceptions"":false,""returnByValue"":false},""id"":1}";
+            return this.SendCommand(json);
+        }
+
+        public string SendCommand(string cmd)
+        {
+            WebSocket4Net.WebSocket j = new WebSocket4Net.WebSocket(this.sessionWSEndpoint);
+            ManualResetEvent waitEvent = new ManualResetEvent(false);
+            ManualResetEvent closedEvent = new ManualResetEvent(false);
+            string message = "";
+            byte[] data;
+
+            Exception exc = null;
+            j.Opened += delegate(System.Object o, EventArgs e)
+            {
+                j.Send(cmd);
+            };
+
+            j.MessageReceived += delegate(System.Object o, MessageReceivedEventArgs e)
+            {
+                message = e.Message;
+                waitEvent.Set();
+            };
+
+            j.Error += delegate(System.Object o, SuperSocket.ClientEngine.ErrorEventArgs e)
+            {
+                exc = e.Exception;
+                waitEvent.Set();
+            };
+
+            j.Closed += delegate(System.Object o, EventArgs e)
+            {
+                closedEvent.Set();
+            };
+
+            j.DataReceived += delegate(System.Object o, DataReceivedEventArgs e)
+            {
+                data = e.Data;
+                waitEvent.Set();
+            };
+
+            j.Open();
+
+            waitEvent.WaitOne();
+            if (j.State == WebSocket4Net.WebSocketState.Open)
+            {
+                j.Close();
+                closedEvent.WaitOne();
+            }
+            if (exc != null)
+                throw exc;
+
+            return message;
+        }
+
+        private T Deserialise<T>(string json)
+        {
+
+            T obj = Activator.CreateInstance<T>();
+            using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
+            {
+                DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
+                obj = (T)serializer.ReadObject(ms);
+                return obj;
+            }
+        }
+
+        private T Deserialise<T>(Stream json)
+        {
+            T obj = Activator.CreateInstance<T>();
+            DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
+            obj = (T)serializer.ReadObject(json);
+            return obj;
+        }
+
+        public void SetActiveSession(string sessionWSEndpoint)
+        {
+            // Sometimes binding to localhost might resolve wrong AddressFamily, force IPv4
+            this.sessionWSEndpoint = sessionWSEndpoint.Replace("ws://localhost", "ws://127.0.0.1");
+
+        }
+    }
+}
\ No newline at end of file
diff --git a/AutoWebPerf/Program.cs b/AutoWebPerf/Program.cs
new file mode 100644
index 0000000000000000000000000000000000000000..94509113471909affdb964ce00f63a5c76e35bb7
--- /dev/null
+++ b/AutoWebPerf/Program.cs
@@ -0,0 +1,104 @@
+using AutoWebPerf.Chrome;
+using AutoWebPerf.Chrome.Messages;
+using AutoWebPerf.Chrome.Messages.Network;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using WebSocket4Net;
+
+namespace AutoWebPerf
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            IChromeProxy _chromeProxy = null;
+            ChromeProcessInfo chromeProcessInfo = null;
+
+            try
+            {
+                chromeProcessInfo = StartChrome();
+
+                var c = new ChromeWrapper("http://localhost:" + ChromeRemoteDebuggingPort);
+                var sessions = c.GetAvailableSessions();
+                foreach (var s in sessions)
+                    Console.WriteLine(s.url);
+
+                if (sessions.Count == 0)
+                    throw new Exception("All debugging sessions are taken.");
+                
+
+
+                // Will drive first tab session
+                var sessionWSEndpoint = sessions.Last().webSocketDebuggerUrl;   // new tab window
+                c.SetActiveSession(sessionWSEndpoint);
+                c.NavigateTo("http://www.google.com");
+                
+                
+                var requestFactory = new RequestFactory();
+                _chromeProxy = new ChromeProxy(sessionWSEndpoint, new ResponseFactory(requestFactory));
+                _chromeProxy.Init().Wait();
+                var result = _chromeProxy.PublishAsync(requestFactory.CreateNetworkEnableRequest()).Result;
+                _chromeProxy.Subscribe<RequestWillBeSentResponse>((o, e) =>
+                        {
+                            Console.WriteLine("We got it!");
+                        });
+                _chromeProxy.PublishAsync(requestFactory.CreateNavigateRequest("http://www.google.com")).Wait();
+                
+
+                Console.ReadLine();
+            }
+            finally
+            {
+                if(null != _chromeProxy)
+                {
+                    _chromeProxy.Dispose();
+                }
+                KillChrome(chromeProcessInfo);
+            }
+
+        }
+
+        private static void KillChrome(ChromeProcessInfo chromeProcessInfo)
+        {
+            try
+            {
+                chromeProcessInfo.ChromeProcess.Kill();
+            } catch
+            {
+                // right now, the process has been killed ... so i'm swallowing this during setup/testing
+            }
+            try
+            {
+                chromeProcessInfo.ChromeUserDirectory.Delete(true);
+            } catch
+            {
+                Thread.Sleep(500);
+                chromeProcessInfo.ChromeUserDirectory.Delete(true);
+            }
+        }
+
+        private static ChromeProcessInfo StartChrome()
+        {
+            string path = Path.GetRandomFileName();
+            var directoryInfo = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), path));
+            var remoteDebuggingArg = "--remote-debugging-port=" + ChromeRemoteDebuggingPort;
+            var userDirectoryArg = "--user-data-dir=\"" + directoryInfo.FullName + "\"";
+            var chromeProcessArgs = remoteDebuggingArg + " " + userDirectoryArg + " --bwsi --no-first-run";
+            var processStartInfo = new ProcessStartInfo(@"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe", chromeProcessArgs);
+            var chromeProcess = Process.Start(processStartInfo);
+            return new ChromeProcessInfo
+            {
+                ChromeProcess = chromeProcess,
+                ChromeUserDirectory = directoryInfo
+            };
+        }
+
+        public const int ChromeRemoteDebuggingPort = 9222;
+    }
+}
diff --git a/AutoWebPerf/Properties/AssemblyInfo.cs b/AutoWebPerf/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8f94ac81291492f939cb821efc3f47a066c40383
--- /dev/null
+++ b/AutoWebPerf/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("AutoWebPerf")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AutoWebPerf")]
+[assembly: AssemblyCopyright("Copyright ©  2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("d4a99e75-3de5-4de5-8bb9-00659c480504")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoWebPerf/RemoveSessions.cs b/AutoWebPerf/RemoveSessions.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f51f32ff6b157e74b3432639ca8934fb09943f31
--- /dev/null
+++ b/AutoWebPerf/RemoveSessions.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoWebPerf
+{
+    [Serializable]
+    [DataContract]
+    public class RemoteSessionsResponse
+    {
+        public RemoteSessionsResponse() { }
+
+        [DataMember]
+        public string devtoolsFrontendUrl;
+
+        [DataMember]
+        public string faviconUrl;
+
+        [DataMember]
+        public string thumbnailUrl;
+
+        [DataMember]
+        public string title;
+
+        [DataMember]
+        public string url;
+
+        [DataMember]
+        public string webSocketDebuggerUrl;
+    }
+}
diff --git a/AutoWebPerf/packages.config b/AutoWebPerf/packages.config
new file mode 100644
index 0000000000000000000000000000000000000000..afd4ccc71de3168b8272ba3b01b94c7c5044cff8
--- /dev/null
+++ b/AutoWebPerf/packages.config
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Newtonsoft.Json" version="6.0.8" targetFramework="net45" />
+  <package id="WebSocket4Net" version="0.12" targetFramework="net45" />
+</packages>
\ No newline at end of file