From 09fac63ffc9927eb5014e5fb5c19fc79236b2ff2 Mon Sep 17 00:00:00 2001
From: chylex <contact@chylex.com>
Date: Sat, 12 Feb 2022 16:30:21 +0100
Subject: [PATCH] Move general CefSharp implementation to a separate library
 project

---
 README.md                                     |  76 +++++++------
 TweetDuck.sln                                 |   6 +
 bld/gen_upd.iss                               |   1 +
 .../BrowserProcessHandler.cs                  |   2 +-
 .../Browser/Base/CefBrowserComponent.cs       |  67 ++---------
 .../{Handling => Base}/ContextMenuBase.cs     |   4 +-
 .../{Handling => Base}/ContextMenuBrowser.cs  |   2 +-
 .../ContextMenuNotification.cs                |   2 +-
 .../CustomJsDialogHandler.cs                  |   2 +-
 .../CustomKeyboardHandler.cs                  |   2 +-
 .../{Handling => Base}/FileDialogHandler.cs   |   2 +-
 .../{Handling => Base}/PopupHandler.cs        |   2 +-
 windows/TweetDuck/Browser/FormBrowser.cs      |   2 +-
 .../Notification/FormNotificationBase.cs      |   2 +-
 .../Notification/FormNotificationMain.cs      |   2 +-
 windows/TweetDuck/Browser/TweetDeckBrowser.cs |   1 -
 windows/TweetDuck/Dialogs/FormGuide.cs        |   1 -
 windows/TweetDuck/Dialogs/FormSettings.cs     |   2 +-
 .../Dialogs/Settings/TabSettingsGeneral.cs    |   2 +-
 windows/TweetDuck/Program.cs                  |   2 +-
 windows/TweetDuck/TweetDuck.csproj            |  47 +++-----
 .../Adapters}/CefAdapter.cs                   |   2 +-
 .../Adapters}/CefBrowserAdapter.cs            |   3 +-
 .../Adapters}/CefDragDataAdapter.cs           |   2 +-
 .../Adapters}/CefErrorCodeAdapter.cs          |   2 +-
 .../Adapters}/CefFrameAdapter.cs              |   2 +-
 .../Adapters}/CefMenuModelAdapter.cs          |   2 +-
 .../Adapters}/CefRequestAdapter.cs            |  19 ++--
 .../Adapters}/CefResponseAdapter.cs           |   2 +-
 .../Component/BrowserComponentBase.cs         |  65 +++++++++++
 .../Handlers}/CefByteArrayResourceHandler.cs  |   7 +-
 .../Handlers}/CefContextMenuHandler.cs        |   5 +-
 .../Handlers}/CefDownloadRequestClient.cs     |   2 +-
 .../Handlers}/CefDragHandler.cs               |   3 +-
 .../Handlers}/CefLifeSpanHandler.cs           |   2 +-
 .../Handlers}/CefRequestHandler.cs            |   3 +-
 .../Handlers}/CefResourceHandlerFactory.cs    |   2 +-
 .../Handlers}/CefResourceRequestHandler.cs    |   3 +-
 .../CefResourceRequestHandlerFactory.cs       |   5 +-
 .../Handlers}/CefResponseFilter.cs            |   2 +-
 .../Handlers}/CefSchemeHandlerFactory.cs      |   5 +-
 .../Properties/AssemblyInfo.cs                |  22 ++++
 .../TweetImpl.CefSharp.csproj                 | 105 ++++++++++++++++++
 windows/TweetImpl.CefSharp/packages.config    |   8 ++
 44 files changed, 332 insertions(+), 170 deletions(-)
 rename windows/TweetDuck/Browser/{Handling => Base}/BrowserProcessHandler.cs (95%)
 rename windows/TweetDuck/Browser/{Handling => Base}/ContextMenuBase.cs (97%)
 rename windows/TweetDuck/Browser/{Handling => Base}/ContextMenuBrowser.cs (99%)
 rename windows/TweetDuck/Browser/{Handling => Base}/ContextMenuNotification.cs (96%)
 rename windows/TweetDuck/Browser/{Handling => Base}/CustomJsDialogHandler.cs (98%)
 rename windows/TweetDuck/Browser/{Handling => Base}/CustomKeyboardHandler.cs (96%)
 rename windows/TweetDuck/Browser/{Handling => Base}/FileDialogHandler.cs (98%)
 rename windows/TweetDuck/Browser/{Handling => Base}/PopupHandler.cs (91%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Adapters}/CefAdapter.cs (88%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Adapters}/CefBrowserAdapter.cs (91%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Adapters}/CefDragDataAdapter.cs (93%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Adapters}/CefErrorCodeAdapter.cs (92%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Adapters}/CefFrameAdapter.cs (93%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Adapters}/CefMenuModelAdapter.cs (96%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Adapters}/CefRequestAdapter.cs (64%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Adapters}/CefResponseAdapter.cs (95%)
 create mode 100644 windows/TweetImpl.CefSharp/Component/BrowserComponentBase.cs
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Handlers}/CefByteArrayResourceHandler.cs (88%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Handlers}/CefContextMenuHandler.cs (89%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Handlers}/CefDownloadRequestClient.cs (96%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Handlers}/CefDragHandler.cs (91%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Handlers}/CefLifeSpanHandler.cs (97%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Handlers}/CefRequestHandler.cs (93%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Handlers}/CefResourceHandlerFactory.cs (93%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Handlers}/CefResourceRequestHandler.cs (95%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Handlers}/CefResourceRequestHandlerFactory.cs (73%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Handlers}/CefResponseFilter.cs (95%)
 rename windows/{TweetDuck/Browser/Base => TweetImpl.CefSharp/Handlers}/CefSchemeHandlerFactory.cs (86%)
 create mode 100644 windows/TweetImpl.CefSharp/Properties/AssemblyInfo.cs
 create mode 100644 windows/TweetImpl.CefSharp/TweetImpl.CefSharp.csproj
 create mode 100644 windows/TweetImpl.CefSharp/packages.config

diff --git a/README.md b/README.md
index 71a343e2..026740a8 100644
--- a/README.md
+++ b/README.md
@@ -8,15 +8,19 @@
         + [Editors](#editors)
         + [Installers](#installers)
     * [Solution Overview](#solution-overview)
-        + [Project: TweetDuck](#project-tweetduck)
-        + [Project: TweetDuck.Browser](#project-tweetduckbrowser)
-        + [Project: TweetDuck.Video](#project-tweetduckvideo)
-        + [Project: TweetLib.Core](#project-tweetlibcore)
-        + [Project: TweetLib.Browser](#project-tweetlibbrowser)
-        + [Project: TweetLib.Browser.CEF](#project-tweetlibbrowsercef)
-        + [Project: TweetLib.Communication](#project-tweetlibcommunication)
-        + [Project: TweetLib.Utils](#project-tweetlibutils)
-        + [Projects: TweetTest.*](#projects-tweettest)
+        + [Core Libraries](#core-libraries)
+            - [TweetLib.Core](#tweetlibcore)
+            - [TweetLib.Browser](#tweetlibbrowser)
+            - [TweetLib.Browser.CEF](#tweetlibbrowsercef)
+        + [Windows Projects](#windows-projects)
+            - [TweetDuck](#tweetduck)
+            - [TweetDuck.Browser](#tweetduckbrowser)
+            - [TweetDuck.Video](#tweetduckvideo)
+            - [TweetImpl.CefSharp](#tweetimplcefsharp)
+        + [Miscellaneous](#miscellaneous)
+            - [TweetLib.Communication](#tweetlibcommunication)
+            - [TweetLib.Utils](#tweetlibutils)
+            - [TweetTest.*](#tweettest)
     * [Development](#development)
         + [Building](#building)
         + [Debugging](#debugging)
@@ -71,14 +75,14 @@ Open the solution file `TweetDuck.sln` in an IDE, and use the **Restore NuGet Pa
 The solution contains several C# projects for executables and libraries, and F# projects for automated tests.
 
 Projects are organized into folders:
-* Windows executables are in the `windows/` folder, and target `.NET Framework 4.7.2` + `C# 8.0`
+* Windows projects are in the `windows/` folder, and target `.NET Framework 4.7.2` + `C# 8.0`
 * Libraries (`TweetLib.*`) are in the `lib/` folder, and target `.NET Standard 2.0` + `C# 9.0`
 * Tests (`TweetTest.*`) are also in the `lib/` folder, and target `.NET Framework 4.7.2` + `F#`
 
 Here are a few things to keep in mind:
 * Executable projects have their entry points in `Program.cs`
-* Library projects have their assembly information in `Lib.cs`
-* Most projects include a link to the `Version.cs` file in the root of the repository, which allows changing the version of all executables and library files in one place
+* Library projects targeting `.NET Standard` have their assembly information in `Lib.cs`
+* All non-test projects include a link to the `Version.cs` file in the root of the repository, which allows changing the version of all executables and library files in one place
 
 Web resource files (HTML, CSS, JS) are in the `Resources/` folder:
 * `Resources/Content/` contains all the core features of TweetDuck injected into the browser components
@@ -87,31 +91,19 @@ Web resource files (HTML, CSS, JS) are in the `Resources/` folder:
 
 These resource folders are linked as part of the `TweetLib.Core` project so they can be edited directly within an IDE. Alternatively, you can edit them using [VS Code](https://code.visualstudio.com/) by opening the workspace file `Resources/..code-workspace`.
 
-### Project: TweetDuck
+### Core Libraries
 
-Main Windows executable. It has a dependency on Windows Forms and [CefSharp](https://github.com/cefsharp/CefSharp/). Here you will mostly find implementations of interfaces from the library projects, and all the Windows and GUI code.
-
-### Project: TweetDuck.Browser
-
-Windows executable that hosts various Chromium processes. It depends on two specific DLLs from the [CefSharp](https://github.com/cefsharp/CefSharp/) package. After updating [CefSharp](https://github.com/cefsharp/CefSharp/), run the `windows/TweetDuck/Resources/PostCefUpdate.ps1` PowerShell script to update these dependencies to the new version.
-
-### Project: TweetDuck.Video
-
-Windows executable that hosts a video player, which is based on the WMPLib ActiveX component responsible for integrating Windows Media Player into .NET Framework.
-
-By default, [CefSharp](https://github.com/cefsharp/CefSharp/) is not built with support for H.264 video playback due to software patent nonsense, and even though TweetDuck could be moved entirely to Europe where MPEG LA's patent means nothing, it would require building a custom version of Chromium which requires too many resources. Instead, when a Twitter video played, TweetDuck launches this video player process, which uses Windows Media Player to play H.264 videos.
-
-### Project: TweetLib.Core
+#### TweetLib.Core
 
 This library contains the core TweetDuck application and browser logic. It is built around simple dependency injection that makes it independent of any concrete OS, GUI framework, or browser implementation.
 
 To simplify porting to other systems, it is not necessary to implement all interfaces, but some functionality will be missing (for ex. if clipboard-related interfaces are not implemented, then context menus will not contain options to copy text or images to clipboard).
 
-### Project: TweetLib.Browser
+#### TweetLib.Browser
 
 This library provides a zero-dependency abstraction of browser components and logic. It defines interfaces, events, and container objects that are used by the `TweetLib.Core` library to describe how a browser should behave, while making as few assumptions about the actual browser implementation as possible.
 
-### Project: TweetLib.Browser.CEF
+#### TweetLib.Browser.CEF
 
 This library is a partial implementation of `TweetLib.Browser` based on [CEF](https://bitbucket.org/chromiumembedded/cef/) interfaces and conventions.
 
@@ -119,15 +111,37 @@ While `TweetLib.Browser` is highly generic, most browser libraries are likely to
 
 Note: The repository contains an experimental `linux` branch, which uses [CefGlue](https://gitlab.com/xiliumhq/chromiumembedded/cefglue) as its browser library instead of [CefSharp](https://github.com/cefsharp/CefSharp/) which only works on Windows.
 
-### Project: TweetLib.Communication
+### Windows Projects
+
+#### TweetDuck
+
+Main Windows executable. It has a dependency on [CefSharp](https://github.com/cefsharp/CefSharp/) and Windows Forms. Here you will find the entry point that bootstraps the main application, as well as code for GUIs and Windows-specific functionality.
+
+#### TweetDuck.Browser
+
+Windows executable that hosts various Chromium processes. It depends on two specific DLLs from the [CefSharp](https://github.com/cefsharp/CefSharp/) package. After updating [CefSharp](https://github.com/cefsharp/CefSharp/), run the `windows/TweetDuck/Resources/PostCefUpdate.ps1` PowerShell script to update these dependencies to the new version.
+
+#### TweetDuck.Video
+
+Windows executable that hosts a video player, which is based on the WMPLib ActiveX component responsible for integrating Windows Media Player into .NET Framework.
+
+By default, [CefSharp](https://github.com/cefsharp/CefSharp/) is not built with support for H.264 video playback due to software patent nonsense, and even though TweetDuck could be moved entirely to Europe where MPEG LA's patent means nothing, it would require building a custom version of Chromium which requires too many resources. Instead, when a Twitter video played, TweetDuck launches this video player process, which uses Windows Media Player to play H.264 videos.
+
+#### TweetImpl.CefSharp
+
+Windows library that implements `TweetLib.Browser.CEF` using the [CefSharp](https://github.com/cefsharp/CefSharp/) library and Windows Forms.
+
+### Miscellaneous
+
+#### TweetLib.Communication
 
 This library provides a `DuplexPipe` class for two-way communication between processes.
 
-### Project: TweetLib.Utils
+#### TweetLib.Utils
 
 This library contains various utilities that fill some very specific holes in the .NET standard library.
 
-### Projects: TweetTest.*
+#### TweetTest.*
 
 These are F# projects with automated tests.
 
diff --git a/TweetDuck.sln b/TweetDuck.sln
index 8ce31dca..94408e6b 100644
--- a/TweetDuck.sln
+++ b/TweetDuck.sln
@@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Browser", "window
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "windows\TweetDuck.Video\TweetDuck.Video.csproj", "{278B2D11-402D-44B6-B6A1-8FA67DB65565}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetImpl.CefSharp", "windows\TweetImpl.CefSharp\TweetImpl.CefSharp.csproj", "{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}"
+EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Core", "lib\TweetLib.Core\TweetLib.Core.csproj", "{93BA3CB4-A812-4949-B07D-8D393FB38937}"
@@ -42,6 +44,10 @@ Global
 		{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.Build.0 = Debug|x86
 		{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.ActiveCfg = Release|x86
 		{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.Build.0 = Release|x86
+		{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}.Debug|x86.ActiveCfg = Debug|x86
+		{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}.Debug|x86.Build.0 = Debug|x86
+		{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}.Release|x86.ActiveCfg = Release|x86
+		{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}.Release|x86.Build.0 = Release|x86
 		{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.ActiveCfg = Debug|x86
 		{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.Build.0 = Debug|x86
 		{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.ActiveCfg = Release|x86
diff --git a/bld/gen_upd.iss b/bld/gen_upd.iss
index f785c625..4a3d805e 100644
--- a/bld/gen_upd.iss
+++ b/bld/gen_upd.iss
@@ -45,6 +45,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
 [Files]
 Source: "..\windows\TweetDuck\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
 Source: "..\windows\TweetDuck\bin\x86\Release\TweetDuck.*"; DestDir: "{app}"; Flags: ignoreversion
+Source: "..\windows\TweetDuck\bin\x86\Release\TweetImpl.*"; DestDir: "{app}"; Flags: ignoreversion
 Source: "..\windows\TweetDuck\bin\x86\Release\TweetLib.*"; DestDir: "{app}"; Flags: ignoreversion
 Source: "..\windows\TweetDuck\bin\x86\Release\guide\*.*"; DestDir: "{app}\guide"; Flags: ignoreversion recursesubdirs createallsubdirs
 Source: "..\windows\TweetDuck\bin\x86\Release\resources\*.*"; DestDir: "{app}\resources"; Flags: ignoreversion recursesubdirs createallsubdirs
diff --git a/windows/TweetDuck/Browser/Handling/BrowserProcessHandler.cs b/windows/TweetDuck/Browser/Base/BrowserProcessHandler.cs
similarity index 95%
rename from windows/TweetDuck/Browser/Handling/BrowserProcessHandler.cs
rename to windows/TweetDuck/Browser/Base/BrowserProcessHandler.cs
index 6054b668..31425c6e 100644
--- a/windows/TweetDuck/Browser/Handling/BrowserProcessHandler.cs
+++ b/windows/TweetDuck/Browser/Base/BrowserProcessHandler.cs
@@ -3,7 +3,7 @@
 using CefSharp;
 using TweetDuck.Configuration;
 
-namespace TweetDuck.Browser.Handling {
+namespace TweetDuck.Browser.Base {
 	sealed class BrowserProcessHandler : IBrowserProcessHandler {
 		public static Task UpdatePrefs() {
 			return Cef.UIThreadTaskFactory.StartNew(UpdatePrefsInternal);
diff --git a/windows/TweetDuck/Browser/Base/CefBrowserComponent.cs b/windows/TweetDuck/Browser/Base/CefBrowserComponent.cs
index d8f7c4c7..58f0e93b 100644
--- a/windows/TweetDuck/Browser/Base/CefBrowserComponent.cs
+++ b/windows/TweetDuck/Browser/Base/CefBrowserComponent.cs
@@ -1,72 +1,23 @@
-using CefSharp;
 using CefSharp.WinForms;
-using TweetDuck.Browser.Handling;
-using TweetDuck.Management;
 using TweetDuck.Utils;
+using TweetImpl.CefSharp.Component;
 using TweetLib.Browser.Base;
-using TweetLib.Browser.CEF.Component;
-using TweetLib.Browser.CEF.Data;
-using IContextMenuHandler = TweetLib.Browser.Interfaces.IContextMenuHandler;
+using TweetLib.Browser.CEF.Utils;
+using TweetLib.Core;
 
 namespace TweetDuck.Browser.Base {
-	sealed class CefBrowserComponent : BrowserComponent<IFrame, IRequest> {
-		public delegate ContextMenuBase CreateContextMenu(IContextMenuHandler handler);
-
+	sealed class CefBrowserComponent : BrowserComponentBase {
 		private static readonly CreateContextMenu DefaultContextMenuFactory = handler => new ContextMenuBase(handler);
 
-		public override string CacheFolder => BrowserCache.CacheFolder;
-
-		public ResourceHandlerRegistry<IResourceHandler> ResourceHandlerRegistry { get; } = new ResourceHandlerRegistry<IResourceHandler>(CefResourceHandlerFactory.Instance);
-
-		private readonly ChromiumWebBrowser browser;
-		private readonly bool autoReload;
-
-		private CreateContextMenu createContextMenu;
-
-		public CefBrowserComponent(ChromiumWebBrowser browser, CreateContextMenu createContextMenu = null, bool autoReload = true) : base(new CefBrowserAdapter(browser), CefAdapter.Instance, CefFrameAdapter.Instance, CefRequestAdapter.Instance) {
-			this.browser = browser;
-			this.browser.LoadingStateChanged += OnLoadingStateChanged;
-			this.browser.LoadError += OnLoadError;
-			this.browser.FrameLoadStart += OnFrameLoadStart;
-			this.browser.FrameLoadEnd += OnFrameLoadEnd;
-			this.browser.SetupZoomEvents();
-			this.createContextMenu = createContextMenu ?? DefaultContextMenuFactory;
-			this.autoReload = autoReload;
+		public override string CacheFolder => CefUtils.GetCacheFolder(App.StoragePath);
+		
+		public CefBrowserComponent(ChromiumWebBrowser browser, CreateContextMenu createContextMenu = null, bool autoReload = true) : base(browser, createContextMenu ?? DefaultContextMenuFactory, PopupHandler.Instance, autoReload) {
+			browser.SetupZoomEvents();	
 		}
 
 		public override void Setup(BrowserSetup setup) {
-			var lifeSpanHandler = new CefLifeSpanHandler(PopupHandler.Instance);
-			var requestHandler = new CefRequestHandler(lifeSpanHandler, autoReload);
-
-			browser.DragHandler = new CefDragHandler(requestHandler, this);
+			base.Setup(setup);
 			browser.JsDialogHandler = new CustomJsDialogHandler();
-			browser.LifeSpanHandler = lifeSpanHandler;
-			browser.MenuHandler = createContextMenu(setup.ContextMenuHandler);
-			browser.RequestHandler = requestHandler;
-			browser.ResourceRequestHandlerFactory = new CefResourceRequestHandlerFactory(setup.ResourceRequestHandler, ResourceHandlerRegistry);
-
-			createContextMenu = null;
-		}
-
-		public override void AttachBridgeObject(string name, object bridge) {
-			browser.JavascriptObjectRepository.Settings.LegacyBindingEnabled = true;
-			browser.JavascriptObjectRepository.Register(name, bridge, isAsync: true, BindingOptions.DefaultBinder);
-		}
-
-		private void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs e) {
-			base.OnLoadingStateChanged(e.IsLoading);
-		}
-
-		private void OnLoadError(object sender, LoadErrorEventArgs e) {
-			base.OnLoadError(e.FailedUrl, e.ErrorCode, CefErrorCodeAdapter.Instance);
-		}
-
-		private void OnFrameLoadStart(object sender, FrameLoadStartEventArgs e) {
-			base.OnFrameLoadStart(e.Url, e.Frame);
-		}
-
-		private void OnFrameLoadEnd(object sender, FrameLoadEndEventArgs e) {
-			base.OnFrameLoadEnd(e.Url, e.Frame);
 		}
 	}
 }
diff --git a/windows/TweetDuck/Browser/Handling/ContextMenuBase.cs b/windows/TweetDuck/Browser/Base/ContextMenuBase.cs
similarity index 97%
rename from windows/TweetDuck/Browser/Handling/ContextMenuBase.cs
rename to windows/TweetDuck/Browser/Base/ContextMenuBase.cs
index 85fb4ed7..96446da8 100644
--- a/windows/TweetDuck/Browser/Handling/ContextMenuBase.cs
+++ b/windows/TweetDuck/Browser/Base/ContextMenuBase.cs
@@ -1,14 +1,14 @@
 using System.Drawing;
 using CefSharp;
-using TweetDuck.Browser.Base;
 using TweetDuck.Configuration;
 using TweetDuck.Utils;
+using TweetImpl.CefSharp.Handlers;
 using TweetLib.Browser.Contexts;
 using TweetLib.Core.Features.TweetDeck;
 using TweetLib.Core.Features.Twitter;
 using IContextMenuHandler = TweetLib.Browser.Interfaces.IContextMenuHandler;
 
-namespace TweetDuck.Browser.Handling {
+namespace TweetDuck.Browser.Base {
 	class ContextMenuBase : CefContextMenuHandler {
 		private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand) 26500;
 
diff --git a/windows/TweetDuck/Browser/Handling/ContextMenuBrowser.cs b/windows/TweetDuck/Browser/Base/ContextMenuBrowser.cs
similarity index 99%
rename from windows/TweetDuck/Browser/Handling/ContextMenuBrowser.cs
rename to windows/TweetDuck/Browser/Base/ContextMenuBrowser.cs
index 50c420cf..5b516038 100644
--- a/windows/TweetDuck/Browser/Handling/ContextMenuBrowser.cs
+++ b/windows/TweetDuck/Browser/Base/ContextMenuBrowser.cs
@@ -7,7 +7,7 @@
 using TweetLib.Core.Systems.Configuration;
 using IContextMenuHandler = TweetLib.Browser.Interfaces.IContextMenuHandler;
 
-namespace TweetDuck.Browser.Handling {
+namespace TweetDuck.Browser.Base {
 	sealed class ContextMenuBrowser : ContextMenuBase {
 		private const CefMenuCommand MenuGlobal   = (CefMenuCommand) 26600;
 		private const CefMenuCommand MenuMute     = (CefMenuCommand) 26601;
diff --git a/windows/TweetDuck/Browser/Handling/ContextMenuNotification.cs b/windows/TweetDuck/Browser/Base/ContextMenuNotification.cs
similarity index 96%
rename from windows/TweetDuck/Browser/Handling/ContextMenuNotification.cs
rename to windows/TweetDuck/Browser/Base/ContextMenuNotification.cs
index 9ab9a155..53f7aec2 100644
--- a/windows/TweetDuck/Browser/Handling/ContextMenuNotification.cs
+++ b/windows/TweetDuck/Browser/Base/ContextMenuNotification.cs
@@ -4,7 +4,7 @@
 using TweetLib.Browser.Contexts;
 using IContextMenuHandler = TweetLib.Browser.Interfaces.IContextMenuHandler;
 
-namespace TweetDuck.Browser.Handling {
+namespace TweetDuck.Browser.Base {
 	sealed class ContextMenuNotification : ContextMenuBase {
 		private readonly FormNotificationBase form;
 
diff --git a/windows/TweetDuck/Browser/Handling/CustomJsDialogHandler.cs b/windows/TweetDuck/Browser/Base/CustomJsDialogHandler.cs
similarity index 98%
rename from windows/TweetDuck/Browser/Handling/CustomJsDialogHandler.cs
rename to windows/TweetDuck/Browser/Base/CustomJsDialogHandler.cs
index c6fd38d9..168b29c6 100644
--- a/windows/TweetDuck/Browser/Handling/CustomJsDialogHandler.cs
+++ b/windows/TweetDuck/Browser/Base/CustomJsDialogHandler.cs
@@ -6,7 +6,7 @@
 using TweetDuck.Dialogs;
 using TweetDuck.Utils;
 
-namespace TweetDuck.Browser.Handling {
+namespace TweetDuck.Browser.Base {
 	sealed class CustomJsDialogHandler : IJsDialogHandler {
 		private static FormMessage CreateMessageForm(string caption, string text) {
 			MessageBoxIcon icon = MessageBoxIcon.None;
diff --git a/windows/TweetDuck/Browser/Handling/CustomKeyboardHandler.cs b/windows/TweetDuck/Browser/Base/CustomKeyboardHandler.cs
similarity index 96%
rename from windows/TweetDuck/Browser/Handling/CustomKeyboardHandler.cs
rename to windows/TweetDuck/Browser/Base/CustomKeyboardHandler.cs
index 58018394..6dd5b98e 100644
--- a/windows/TweetDuck/Browser/Handling/CustomKeyboardHandler.cs
+++ b/windows/TweetDuck/Browser/Base/CustomKeyboardHandler.cs
@@ -3,7 +3,7 @@
 using TweetDuck.Utils;
 using TweetLib.Utils.Static;
 
-namespace TweetDuck.Browser.Handling {
+namespace TweetDuck.Browser.Base {
 	sealed class CustomKeyboardHandler : IKeyboardHandler {
 		private readonly IBrowserKeyHandler handler;
 
diff --git a/windows/TweetDuck/Browser/Handling/FileDialogHandler.cs b/windows/TweetDuck/Browser/Base/FileDialogHandler.cs
similarity index 98%
rename from windows/TweetDuck/Browser/Handling/FileDialogHandler.cs
rename to windows/TweetDuck/Browser/Base/FileDialogHandler.cs
index 29676aa2..e1c1a631 100644
--- a/windows/TweetDuck/Browser/Handling/FileDialogHandler.cs
+++ b/windows/TweetDuck/Browser/Base/FileDialogHandler.cs
@@ -7,7 +7,7 @@
 using TweetLib.Core;
 using TweetLib.Utils.Static;
 
-namespace TweetDuck.Browser.Handling {
+namespace TweetDuck.Browser.Base {
 	sealed class FileDialogHandler : IDialogHandler {
 		public bool OnFileDialog(IWebBrowser browserControl, IBrowser browser, CefFileDialogMode mode, CefFileDialogFlags flags, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback) {
 			if (mode == CefFileDialogMode.Open || mode == CefFileDialogMode.OpenMultiple) {
diff --git a/windows/TweetDuck/Browser/Handling/PopupHandler.cs b/windows/TweetDuck/Browser/Base/PopupHandler.cs
similarity index 91%
rename from windows/TweetDuck/Browser/Handling/PopupHandler.cs
rename to windows/TweetDuck/Browser/Base/PopupHandler.cs
index 1871d072..65987ec1 100644
--- a/windows/TweetDuck/Browser/Handling/PopupHandler.cs
+++ b/windows/TweetDuck/Browser/Base/PopupHandler.cs
@@ -2,7 +2,7 @@
 using TweetLib.Core;
 using TweetLib.Core.Features.Twitter;
 
-namespace TweetDuck.Browser.Handling {
+namespace TweetDuck.Browser.Base {
 	sealed class PopupHandler : IPopupHandler {
 		public static PopupHandler Instance { get; } = new PopupHandler();
 
diff --git a/windows/TweetDuck/Browser/FormBrowser.cs b/windows/TweetDuck/Browser/FormBrowser.cs
index e7aad6e2..16e05468 100644
--- a/windows/TweetDuck/Browser/FormBrowser.cs
+++ b/windows/TweetDuck/Browser/FormBrowser.cs
@@ -6,7 +6,7 @@
 using System.Threading.Tasks;
 using System.Windows.Forms;
 using CefSharp;
-using TweetDuck.Browser.Handling;
+using TweetDuck.Browser.Base;
 using TweetDuck.Browser.Notification;
 using TweetDuck.Browser.Notification.Screenshot;
 using TweetDuck.Configuration;
diff --git a/windows/TweetDuck/Browser/Notification/FormNotificationBase.cs b/windows/TweetDuck/Browser/Notification/FormNotificationBase.cs
index 40121290..188dd5b7 100644
--- a/windows/TweetDuck/Browser/Notification/FormNotificationBase.cs
+++ b/windows/TweetDuck/Browser/Notification/FormNotificationBase.cs
@@ -4,10 +4,10 @@
 using System.Windows.Forms;
 using CefSharp.WinForms;
 using TweetDuck.Browser.Base;
-using TweetDuck.Browser.Handling;
 using TweetDuck.Configuration;
 using TweetDuck.Controls;
 using TweetDuck.Utils;
+using TweetImpl.CefSharp.Handlers;
 using TweetLib.Browser.CEF.Data;
 using TweetLib.Browser.Interfaces;
 using TweetLib.Core.Features.Notifications;
diff --git a/windows/TweetDuck/Browser/Notification/FormNotificationMain.cs b/windows/TweetDuck/Browser/Notification/FormNotificationMain.cs
index 997184d9..386bb1ea 100644
--- a/windows/TweetDuck/Browser/Notification/FormNotificationMain.cs
+++ b/windows/TweetDuck/Browser/Notification/FormNotificationMain.cs
@@ -2,7 +2,7 @@
 using System.Drawing;
 using System.Windows.Forms;
 using CefSharp;
-using TweetDuck.Browser.Handling;
+using TweetDuck.Browser.Base;
 using TweetDuck.Controls;
 using TweetDuck.Utils;
 using TweetLib.Core.Features.Notifications;
diff --git a/windows/TweetDuck/Browser/TweetDeckBrowser.cs b/windows/TweetDuck/Browser/TweetDeckBrowser.cs
index 08957132..4c0ffb63 100644
--- a/windows/TweetDuck/Browser/TweetDeckBrowser.cs
+++ b/windows/TweetDuck/Browser/TweetDeckBrowser.cs
@@ -3,7 +3,6 @@
 using CefSharp;
 using CefSharp.WinForms;
 using TweetDuck.Browser.Base;
-using TweetDuck.Browser.Handling;
 using TweetDuck.Browser.Notification;
 using TweetDuck.Configuration;
 using TweetDuck.Utils;
diff --git a/windows/TweetDuck/Dialogs/FormGuide.cs b/windows/TweetDuck/Dialogs/FormGuide.cs
index 94ffb731..49c1baa6 100644
--- a/windows/TweetDuck/Dialogs/FormGuide.cs
+++ b/windows/TweetDuck/Dialogs/FormGuide.cs
@@ -3,7 +3,6 @@
 using CefSharp.WinForms;
 using TweetDuck.Browser;
 using TweetDuck.Browser.Base;
-using TweetDuck.Browser.Handling;
 using TweetDuck.Controls;
 using TweetDuck.Management;
 using TweetDuck.Utils;
diff --git a/windows/TweetDuck/Dialogs/FormSettings.cs b/windows/TweetDuck/Dialogs/FormSettings.cs
index 50451874..463e2fe9 100644
--- a/windows/TweetDuck/Dialogs/FormSettings.cs
+++ b/windows/TweetDuck/Dialogs/FormSettings.cs
@@ -3,7 +3,7 @@
 using System.Drawing;
 using System.Windows.Forms;
 using TweetDuck.Browser;
-using TweetDuck.Browser.Handling;
+using TweetDuck.Browser.Base;
 using TweetDuck.Browser.Notification;
 using TweetDuck.Configuration;
 using TweetDuck.Controls;
diff --git a/windows/TweetDuck/Dialogs/Settings/TabSettingsGeneral.cs b/windows/TweetDuck/Dialogs/Settings/TabSettingsGeneral.cs
index d2687e21..435d4e45 100644
--- a/windows/TweetDuck/Dialogs/Settings/TabSettingsGeneral.cs
+++ b/windows/TweetDuck/Dialogs/Settings/TabSettingsGeneral.cs
@@ -2,7 +2,7 @@
 using System.IO;
 using System.Linq;
 using System.Windows.Forms;
-using TweetDuck.Browser.Handling;
+using TweetDuck.Browser.Base;
 using TweetDuck.Controls;
 using TweetDuck.Utils;
 using TweetLib.Core;
diff --git a/windows/TweetDuck/Program.cs b/windows/TweetDuck/Program.cs
index 4818e905..07281684 100644
--- a/windows/TweetDuck/Program.cs
+++ b/windows/TweetDuck/Program.cs
@@ -6,12 +6,12 @@
 using TweetDuck.Application;
 using TweetDuck.Browser;
 using TweetDuck.Browser.Base;
-using TweetDuck.Browser.Handling;
 using TweetDuck.Configuration;
 using TweetDuck.Dialogs;
 using TweetDuck.Management;
 using TweetDuck.Updates;
 using TweetDuck.Utils;
+using TweetImpl.CefSharp.Handlers;
 using TweetLib.Browser.CEF.Utils;
 using TweetLib.Core;
 using TweetLib.Core.Application;
diff --git a/windows/TweetDuck/TweetDuck.csproj b/windows/TweetDuck/TweetDuck.csproj
index 4179c715..b1294302 100644
--- a/windows/TweetDuck/TweetDuck.csproj
+++ b/windows/TweetDuck/TweetDuck.csproj
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Import Project="..\..\packages\CefSharp.Common.96.0.180\build\CefSharp.Common.props" Condition="Exists('..\..\packages\CefSharp.Common.96.0.180\build\CefSharp.Common.props')" />
   <Import Project="..\..\packages\cef.redist.x86.96.0.18\build\cef.redist.x86.props" Condition="Exists('..\..\packages\cef.redist.x86.96.0.18\build\cef.redist.x86.props')" />
   <Import Project="..\..\packages\cef.redist.x64.96.0.18\build\cef.redist.x64.props" Condition="Exists('..\..\packages\cef.redist.x64.96.0.18\build\cef.redist.x64.props')" />
@@ -45,13 +45,13 @@
     <Prefer32Bit>false</Prefer32Bit>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="CefSharp, Version=96.0.180.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
+    <Reference Include="CefSharp, Version=96.0.180.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138">
       <HintPath>..\..\packages\CefSharp.Common.96.0.180\lib\net452\CefSharp.dll</HintPath>
     </Reference>
-    <Reference Include="CefSharp.Core, Version=96.0.180.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
+    <Reference Include="CefSharp.Core, Version=96.0.180.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138">
       <HintPath>..\..\packages\CefSharp.Common.96.0.180\lib\net452\CefSharp.Core.dll</HintPath>
     </Reference>
-    <Reference Include="CefSharp.WinForms, Version=96.0.180.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
+    <Reference Include="CefSharp.WinForms, Version=96.0.180.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138">
       <HintPath>..\..\packages\CefSharp.WinForms.96.0.180\lib\net462\CefSharp.WinForms.dll</HintPath>
     </Reference>
     <Reference Include="System" />
@@ -90,39 +90,24 @@
       <Project>{72473763-4b9d-4fb6-a923-9364b2680f06}</Project>
       <Name>TweetLib.Communication</Name>
     </ProjectReference>
+    <ProjectReference Include="..\TweetImpl.CefSharp\TweetImpl.CefSharp.csproj">
+      <Project>{44df3e2e-f465-4a31-8b43-f40fffb018ba}</Project>
+      <Name>TweetImpl.CefSharp</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Application\FileDialogs.cs" />
     <Compile Include="Application\MessageDialogs.cs" />
     <Compile Include="Application\SystemHandler.cs" />
-    <Compile Include="Browser\Base\CefAdapter.cs" />
-    <Compile Include="Browser\Base\CefBrowserAdapter.cs" />
+    <Compile Include="Browser\Base\BrowserProcessHandler.cs" />
     <Compile Include="Browser\Base\CefBrowserComponent.cs" />
-    <Compile Include="Browser\Base\CefContextMenuHandler.cs" />
-    <Compile Include="Browser\Base\CefDownloadRequestClient.cs" />
-    <Compile Include="Browser\Base\CefDragDataAdapter.cs" />
-    <Compile Include="Browser\Base\CefDragHandler.cs" />
-    <Compile Include="Browser\Base\CefErrorCodeAdapter.cs" />
-    <Compile Include="Browser\Base\CefFrameAdapter.cs" />
-    <Compile Include="Browser\Base\CefLifeSpanHandler.cs" />
-    <Compile Include="Browser\Base\CefMenuModelAdapter.cs" />
-    <Compile Include="Browser\Base\CefRequestAdapter.cs" />
-    <Compile Include="Browser\Base\CefRequestHandler.cs" />
-    <Compile Include="Browser\Base\CefResourceHandlerFactory.cs" />
-    <Compile Include="Browser\Base\CefByteArrayResourceHandler.cs" />
-    <Compile Include="Browser\Base\CefResourceRequestHandlerFactory.cs" />
-    <Compile Include="Browser\Base\CefResourceRequestHandler.cs" />
-    <Compile Include="Browser\Base\CefResponseAdapter.cs" />
-    <Compile Include="Browser\Base\CefResponseFilter.cs" />
-    <Compile Include="Browser\Base\CefSchemeHandlerFactory.cs" />
-    <Compile Include="Browser\Handling\BrowserProcessHandler.cs" />
-    <Compile Include="Browser\Handling\ContextMenuBase.cs" />
-    <Compile Include="Browser\Handling\ContextMenuBrowser.cs" />
-    <Compile Include="Browser\Handling\ContextMenuNotification.cs" />
-    <Compile Include="Browser\Handling\CustomKeyboardHandler.cs" />
-    <Compile Include="Browser\Handling\FileDialogHandler.cs" />
-    <Compile Include="Browser\Handling\CustomJsDialogHandler.cs" />
-    <Compile Include="Browser\Handling\PopupHandler.cs" />
+    <Compile Include="Browser\Base\ContextMenuBase.cs" />
+    <Compile Include="Browser\Base\ContextMenuBrowser.cs" />
+    <Compile Include="Browser\Base\ContextMenuNotification.cs" />
+    <Compile Include="Browser\Base\CustomKeyboardHandler.cs" />
+    <Compile Include="Browser\Base\FileDialogHandler.cs" />
+    <Compile Include="Browser\Base\CustomJsDialogHandler.cs" />
+    <Compile Include="Browser\Base\PopupHandler.cs" />
     <Compile Include="Browser\Notification\FormNotificationExample.cs">
       <SubType>Form</SubType>
     </Compile>
diff --git a/windows/TweetDuck/Browser/Base/CefAdapter.cs b/windows/TweetImpl.CefSharp/Adapters/CefAdapter.cs
similarity index 88%
rename from windows/TweetDuck/Browser/Base/CefAdapter.cs
rename to windows/TweetImpl.CefSharp/Adapters/CefAdapter.cs
index 06aa6dc8..1dbd2804 100644
--- a/windows/TweetDuck/Browser/Base/CefAdapter.cs
+++ b/windows/TweetImpl.CefSharp/Adapters/CefAdapter.cs
@@ -2,7 +2,7 @@
 using CefSharp;
 using TweetLib.Browser.CEF.Interfaces;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Adapters {
 	sealed class CefAdapter : ICefAdapter {
 		public static CefAdapter Instance { get; } = new CefAdapter();
 
diff --git a/windows/TweetDuck/Browser/Base/CefBrowserAdapter.cs b/windows/TweetImpl.CefSharp/Adapters/CefBrowserAdapter.cs
similarity index 91%
rename from windows/TweetDuck/Browser/Base/CefBrowserAdapter.cs
rename to windows/TweetImpl.CefSharp/Adapters/CefBrowserAdapter.cs
index abccada1..1017c9e9 100644
--- a/windows/TweetDuck/Browser/Base/CefBrowserAdapter.cs
+++ b/windows/TweetImpl.CefSharp/Adapters/CefBrowserAdapter.cs
@@ -1,9 +1,10 @@
 using CefSharp;
 using CefSharp.WinForms;
+using TweetImpl.CefSharp.Handlers;
 using TweetLib.Browser.CEF.Data;
 using TweetLib.Browser.CEF.Interfaces;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Adapters {
 	sealed class CefBrowserAdapter : IBrowserWrapper<IFrame, IRequest> {
 		public string Url => browser.Address;
 		public IFrame MainFrame => browser.GetMainFrame();
diff --git a/windows/TweetDuck/Browser/Base/CefDragDataAdapter.cs b/windows/TweetImpl.CefSharp/Adapters/CefDragDataAdapter.cs
similarity index 93%
rename from windows/TweetDuck/Browser/Base/CefDragDataAdapter.cs
rename to windows/TweetImpl.CefSharp/Adapters/CefDragDataAdapter.cs
index 89fdc1c1..bd3d17bb 100644
--- a/windows/TweetDuck/Browser/Base/CefDragDataAdapter.cs
+++ b/windows/TweetImpl.CefSharp/Adapters/CefDragDataAdapter.cs
@@ -1,7 +1,7 @@
 using CefSharp;
 using TweetLib.Browser.CEF.Interfaces;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Adapters {
 	sealed class CefDragDataAdapter : IDragDataAdapter<IDragData> {
 		public static CefDragDataAdapter Instance { get; } = new CefDragDataAdapter();
 
diff --git a/windows/TweetDuck/Browser/Base/CefErrorCodeAdapter.cs b/windows/TweetImpl.CefSharp/Adapters/CefErrorCodeAdapter.cs
similarity index 92%
rename from windows/TweetDuck/Browser/Base/CefErrorCodeAdapter.cs
rename to windows/TweetImpl.CefSharp/Adapters/CefErrorCodeAdapter.cs
index 85876246..39a35494 100644
--- a/windows/TweetDuck/Browser/Base/CefErrorCodeAdapter.cs
+++ b/windows/TweetImpl.CefSharp/Adapters/CefErrorCodeAdapter.cs
@@ -2,7 +2,7 @@
 using CefSharp;
 using TweetLib.Browser.CEF.Interfaces;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Adapters {
 	sealed class CefErrorCodeAdapter : IErrorCodeAdapter<CefErrorCode> {
 		public static CefErrorCodeAdapter Instance { get; } = new CefErrorCodeAdapter();
 
diff --git a/windows/TweetDuck/Browser/Base/CefFrameAdapter.cs b/windows/TweetImpl.CefSharp/Adapters/CefFrameAdapter.cs
similarity index 93%
rename from windows/TweetDuck/Browser/Base/CefFrameAdapter.cs
rename to windows/TweetImpl.CefSharp/Adapters/CefFrameAdapter.cs
index d78a32ed..c1a2a091 100644
--- a/windows/TweetDuck/Browser/Base/CefFrameAdapter.cs
+++ b/windows/TweetImpl.CefSharp/Adapters/CefFrameAdapter.cs
@@ -1,7 +1,7 @@
 using CefSharp;
 using TweetLib.Browser.CEF.Interfaces;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Adapters {
 	sealed class CefFrameAdapter : IFrameAdapter<IFrame> {
 		public static CefFrameAdapter Instance { get; } = new CefFrameAdapter();
 
diff --git a/windows/TweetDuck/Browser/Base/CefMenuModelAdapter.cs b/windows/TweetImpl.CefSharp/Adapters/CefMenuModelAdapter.cs
similarity index 96%
rename from windows/TweetDuck/Browser/Base/CefMenuModelAdapter.cs
rename to windows/TweetImpl.CefSharp/Adapters/CefMenuModelAdapter.cs
index b38a5fd7..03b300eb 100644
--- a/windows/TweetDuck/Browser/Base/CefMenuModelAdapter.cs
+++ b/windows/TweetImpl.CefSharp/Adapters/CefMenuModelAdapter.cs
@@ -1,7 +1,7 @@
 using CefSharp;
 using TweetLib.Browser.CEF.Interfaces;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Adapters {
 	sealed class CefMenuModelAdapter : IMenuModelAdapter<IMenuModel> {
 		public static CefMenuModelAdapter Instance { get; } = new CefMenuModelAdapter();
 
diff --git a/windows/TweetDuck/Browser/Base/CefRequestAdapter.cs b/windows/TweetImpl.CefSharp/Adapters/CefRequestAdapter.cs
similarity index 64%
rename from windows/TweetDuck/Browser/Base/CefRequestAdapter.cs
rename to windows/TweetImpl.CefSharp/Adapters/CefRequestAdapter.cs
index ff0361b0..6e9edce7 100644
--- a/windows/TweetDuck/Browser/Base/CefRequestAdapter.cs
+++ b/windows/TweetImpl.CefSharp/Adapters/CefRequestAdapter.cs
@@ -1,8 +1,7 @@
 using CefSharp;
 using TweetLib.Browser.CEF.Interfaces;
-using ResourceType = TweetLib.Browser.Request.ResourceType;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Adapters {
 	sealed class CefRequestAdapter : IRequestAdapter<IRequest> {
 		public static CefRequestAdapter Instance { get; } = new CefRequestAdapter();
 
@@ -29,17 +28,17 @@ public bool IsTransitionForwardBack(IRequest request) {
 		}
 
 		public bool IsCspReport(IRequest request) {
-			return request.ResourceType == CefSharp.ResourceType.CspReport;
+			return request.ResourceType == ResourceType.CspReport;
 		}
 
-		public ResourceType GetResourceType(IRequest request) {
+		public TweetLib.Browser.Request.ResourceType GetResourceType(IRequest request) {
 			return request.ResourceType switch {
-				CefSharp.ResourceType.MainFrame  => ResourceType.MainFrame,
-				CefSharp.ResourceType.Script     => ResourceType.Script,
-				CefSharp.ResourceType.Stylesheet => ResourceType.Stylesheet,
-				CefSharp.ResourceType.Xhr        => ResourceType.Xhr,
-				CefSharp.ResourceType.Image      => ResourceType.Image,
-				_                                => ResourceType.Unknown
+				ResourceType.MainFrame  => TweetLib.Browser.Request.ResourceType.MainFrame,
+				ResourceType.Script     => TweetLib.Browser.Request.ResourceType.Script,
+				ResourceType.Stylesheet => TweetLib.Browser.Request.ResourceType.Stylesheet,
+				ResourceType.Xhr        => TweetLib.Browser.Request.ResourceType.Xhr,
+				ResourceType.Image      => TweetLib.Browser.Request.ResourceType.Image,
+				_                       => TweetLib.Browser.Request.ResourceType.Unknown
 			};
 		}
 
diff --git a/windows/TweetDuck/Browser/Base/CefResponseAdapter.cs b/windows/TweetImpl.CefSharp/Adapters/CefResponseAdapter.cs
similarity index 95%
rename from windows/TweetDuck/Browser/Base/CefResponseAdapter.cs
rename to windows/TweetImpl.CefSharp/Adapters/CefResponseAdapter.cs
index 1a5ca59b..ce54f7f4 100644
--- a/windows/TweetDuck/Browser/Base/CefResponseAdapter.cs
+++ b/windows/TweetImpl.CefSharp/Adapters/CefResponseAdapter.cs
@@ -1,7 +1,7 @@
 using CefSharp;
 using TweetLib.Browser.CEF.Interfaces;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Adapters {
 	sealed class CefResponseAdapter : IResponseAdapter<IResponse> {
 		public static CefResponseAdapter Instance { get; } = new CefResponseAdapter();
 
diff --git a/windows/TweetImpl.CefSharp/Component/BrowserComponentBase.cs b/windows/TweetImpl.CefSharp/Component/BrowserComponentBase.cs
new file mode 100644
index 00000000..372bdb30
--- /dev/null
+++ b/windows/TweetImpl.CefSharp/Component/BrowserComponentBase.cs
@@ -0,0 +1,65 @@
+using CefSharp;
+using CefSharp.WinForms;
+using TweetImpl.CefSharp.Adapters;
+using TweetImpl.CefSharp.Handlers;
+using TweetLib.Browser.Base;
+using TweetLib.Browser.CEF.Component;
+using TweetLib.Browser.CEF.Data;
+using TweetLib.Browser.CEF.Interfaces;
+using IContextMenuHandler = TweetLib.Browser.Interfaces.IContextMenuHandler;
+
+namespace TweetImpl.CefSharp.Component {
+	public abstract class BrowserComponentBase : BrowserComponent<IFrame, IRequest> {
+		public delegate CefContextMenuHandler CreateContextMenu(IContextMenuHandler handler);
+
+		public ResourceHandlerRegistry<IResourceHandler> ResourceHandlerRegistry { get; } = new ResourceHandlerRegistry<IResourceHandler>(CefResourceHandlerFactory.Instance);
+
+		protected readonly ChromiumWebBrowser browser;
+		private readonly CreateContextMenu createContextMenu;
+		private readonly IPopupHandler popupHandler;
+		private readonly bool autoReload;
+
+		protected BrowserComponentBase(ChromiumWebBrowser browser, CreateContextMenu createContextMenu, IPopupHandler popupHandler, bool autoReload) : base(new CefBrowserAdapter(browser), CefAdapter.Instance, CefFrameAdapter.Instance, CefRequestAdapter.Instance) {
+			this.browser = browser;
+			this.browser.LoadingStateChanged += OnLoadingStateChanged;
+			this.browser.LoadError += OnLoadError;
+			this.browser.FrameLoadStart += OnFrameLoadStart;
+			this.browser.FrameLoadEnd += OnFrameLoadEnd;
+			this.createContextMenu = createContextMenu;
+			this.popupHandler = popupHandler;
+			this.autoReload = autoReload;
+		}
+
+		public override void Setup(BrowserSetup setup) {
+			var lifeSpanHandler = new CefLifeSpanHandler(popupHandler);
+			var requestHandler = new CefRequestHandler(lifeSpanHandler, autoReload);
+
+			browser.DragHandler = new CefDragHandler(requestHandler, this);
+			browser.LifeSpanHandler = lifeSpanHandler;
+			browser.MenuHandler = createContextMenu(setup.ContextMenuHandler);
+			browser.RequestHandler = requestHandler;
+			browser.ResourceRequestHandlerFactory = new CefResourceRequestHandlerFactory(setup.ResourceRequestHandler, ResourceHandlerRegistry);
+		}
+
+		public override void AttachBridgeObject(string name, object bridge) {
+			browser.JavascriptObjectRepository.Settings.LegacyBindingEnabled = true;
+			browser.JavascriptObjectRepository.Register(name, bridge, isAsync: true, BindingOptions.DefaultBinder);
+		}
+
+		private void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs e) {
+			base.OnLoadingStateChanged(e.IsLoading);
+		}
+
+		private void OnLoadError(object sender, LoadErrorEventArgs e) {
+			base.OnLoadError(e.FailedUrl, e.ErrorCode, CefErrorCodeAdapter.Instance);
+		}
+
+		private void OnFrameLoadStart(object sender, FrameLoadStartEventArgs e) {
+			base.OnFrameLoadStart(e.Url, e.Frame);
+		}
+
+		private void OnFrameLoadEnd(object sender, FrameLoadEndEventArgs e) {
+			base.OnFrameLoadEnd(e.Url, e.Frame);
+		}
+	}
+}
diff --git a/windows/TweetDuck/Browser/Base/CefByteArrayResourceHandler.cs b/windows/TweetImpl.CefSharp/Handlers/CefByteArrayResourceHandler.cs
similarity index 88%
rename from windows/TweetDuck/Browser/Base/CefByteArrayResourceHandler.cs
rename to windows/TweetImpl.CefSharp/Handlers/CefByteArrayResourceHandler.cs
index d7874b30..8a429f9b 100644
--- a/windows/TweetDuck/Browser/Base/CefByteArrayResourceHandler.cs
+++ b/windows/TweetImpl.CefSharp/Handlers/CefByteArrayResourceHandler.cs
@@ -2,11 +2,12 @@
 using System.IO;
 using CefSharp;
 using CefSharp.Callback;
+using TweetImpl.CefSharp.Adapters;
 using TweetLib.Browser.CEF.Data;
 using TweetLib.Browser.CEF.Logic;
 
-namespace TweetDuck.Browser.Base {
-	sealed class CefByteArrayResourceHandler : IResourceHandler {
+namespace TweetImpl.CefSharp.Handlers {
+	public sealed class CefByteArrayResourceHandler : IResourceHandler {
 		private static readonly ByteArrayResourceHandlerLogic.WriteToOut<Stream> WriteToOut = delegate (Stream dataOut, byte[] dataIn, int position, int length) {
 			dataOut.Write(dataIn, position, length);
 		};
@@ -17,7 +18,7 @@ public CefByteArrayResourceHandler() {
 			SetResource(new ByteArrayResource(Array.Empty<byte>()));
 		}
 
-		public CefByteArrayResourceHandler(ByteArrayResource resource) {
+		internal CefByteArrayResourceHandler(ByteArrayResource resource) {
 			SetResource(resource);
 		}
 
diff --git a/windows/TweetDuck/Browser/Base/CefContextMenuHandler.cs b/windows/TweetImpl.CefSharp/Handlers/CefContextMenuHandler.cs
similarity index 89%
rename from windows/TweetDuck/Browser/Base/CefContextMenuHandler.cs
rename to windows/TweetImpl.CefSharp/Handlers/CefContextMenuHandler.cs
index 620fd5f4..b1cb7927 100644
--- a/windows/TweetDuck/Browser/Base/CefContextMenuHandler.cs
+++ b/windows/TweetImpl.CefSharp/Handlers/CefContextMenuHandler.cs
@@ -1,9 +1,10 @@
 using CefSharp;
+using TweetImpl.CefSharp.Adapters;
 using TweetLib.Browser.CEF.Logic;
 using TweetLib.Browser.Contexts;
 
-namespace TweetDuck.Browser.Base {
-	abstract class CefContextMenuHandler : IContextMenuHandler {
+namespace TweetImpl.CefSharp.Handlers {
+	public abstract class CefContextMenuHandler : IContextMenuHandler {
 		private readonly ContextMenuLogic<IMenuModel> logic;
 
 		protected CefContextMenuHandler(TweetLib.Browser.Interfaces.IContextMenuHandler handler) {
diff --git a/windows/TweetDuck/Browser/Base/CefDownloadRequestClient.cs b/windows/TweetImpl.CefSharp/Handlers/CefDownloadRequestClient.cs
similarity index 96%
rename from windows/TweetDuck/Browser/Base/CefDownloadRequestClient.cs
rename to windows/TweetImpl.CefSharp/Handlers/CefDownloadRequestClient.cs
index ef97f03b..ab49ef78 100644
--- a/windows/TweetDuck/Browser/Base/CefDownloadRequestClient.cs
+++ b/windows/TweetImpl.CefSharp/Handlers/CefDownloadRequestClient.cs
@@ -4,7 +4,7 @@
 using TweetLib.Browser.CEF.Logic;
 using static TweetLib.Browser.CEF.Logic.DownloadRequestClientLogic.RequestStatus;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Handlers {
 	sealed class CefDownloadRequestClient : UrlRequestClient {
 		private readonly DownloadRequestClientLogic logic;
 
diff --git a/windows/TweetDuck/Browser/Base/CefDragHandler.cs b/windows/TweetImpl.CefSharp/Handlers/CefDragHandler.cs
similarity index 91%
rename from windows/TweetDuck/Browser/Base/CefDragHandler.cs
rename to windows/TweetImpl.CefSharp/Handlers/CefDragHandler.cs
index 09b1a679..5b22c13d 100644
--- a/windows/TweetDuck/Browser/Base/CefDragHandler.cs
+++ b/windows/TweetImpl.CefSharp/Handlers/CefDragHandler.cs
@@ -1,10 +1,11 @@
 using System.Collections.Generic;
 using CefSharp;
 using CefSharp.Enums;
+using TweetImpl.CefSharp.Adapters;
 using TweetLib.Browser.CEF.Logic;
 using TweetLib.Browser.Interfaces;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Handlers {
 	sealed class CefDragHandler : IDragHandler {
 		private readonly DragHandlerLogic<IDragData, IRequest> logic;
 
diff --git a/windows/TweetDuck/Browser/Base/CefLifeSpanHandler.cs b/windows/TweetImpl.CefSharp/Handlers/CefLifeSpanHandler.cs
similarity index 97%
rename from windows/TweetDuck/Browser/Base/CefLifeSpanHandler.cs
rename to windows/TweetImpl.CefSharp/Handlers/CefLifeSpanHandler.cs
index 28f5c76b..35f7f2eb 100644
--- a/windows/TweetDuck/Browser/Base/CefLifeSpanHandler.cs
+++ b/windows/TweetImpl.CefSharp/Handlers/CefLifeSpanHandler.cs
@@ -4,7 +4,7 @@
 using TweetLib.Browser.CEF.Logic;
 using static TweetLib.Browser.CEF.Logic.LifeSpanHandlerLogic.TargetDisposition;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Handlers {
 	sealed class CefLifeSpanHandler : LifeSpanHandler {
 		public LifeSpanHandlerLogic Logic { get; }
 
diff --git a/windows/TweetDuck/Browser/Base/CefRequestHandler.cs b/windows/TweetImpl.CefSharp/Handlers/CefRequestHandler.cs
similarity index 93%
rename from windows/TweetDuck/Browser/Base/CefRequestHandler.cs
rename to windows/TweetImpl.CefSharp/Handlers/CefRequestHandler.cs
index 070ee897..a21e5c2d 100644
--- a/windows/TweetDuck/Browser/Base/CefRequestHandler.cs
+++ b/windows/TweetImpl.CefSharp/Handlers/CefRequestHandler.cs
@@ -1,8 +1,9 @@
 using CefSharp;
 using CefSharp.Handler;
+using TweetImpl.CefSharp.Adapters;
 using TweetLib.Browser.CEF.Logic;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Handlers {
 	sealed class CefRequestHandler : RequestHandler {
 		public RequestHandlerLogic<IRequest> Logic { get; }
 
diff --git a/windows/TweetDuck/Browser/Base/CefResourceHandlerFactory.cs b/windows/TweetImpl.CefSharp/Handlers/CefResourceHandlerFactory.cs
similarity index 93%
rename from windows/TweetDuck/Browser/Base/CefResourceHandlerFactory.cs
rename to windows/TweetImpl.CefSharp/Handlers/CefResourceHandlerFactory.cs
index c087df21..bbcffcbe 100644
--- a/windows/TweetDuck/Browser/Base/CefResourceHandlerFactory.cs
+++ b/windows/TweetImpl.CefSharp/Handlers/CefResourceHandlerFactory.cs
@@ -2,7 +2,7 @@
 using TweetLib.Browser.CEF.Data;
 using TweetLib.Browser.CEF.Interfaces;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Handlers {
 	sealed class CefResourceHandlerFactory : IResourceHandlerFactory<IResourceHandler> {
 		public static CefResourceHandlerFactory Instance { get; } = new CefResourceHandlerFactory();
 
diff --git a/windows/TweetDuck/Browser/Base/CefResourceRequestHandler.cs b/windows/TweetImpl.CefSharp/Handlers/CefResourceRequestHandler.cs
similarity index 95%
rename from windows/TweetDuck/Browser/Base/CefResourceRequestHandler.cs
rename to windows/TweetImpl.CefSharp/Handlers/CefResourceRequestHandler.cs
index 6ac55e5b..15d9212f 100644
--- a/windows/TweetDuck/Browser/Base/CefResourceRequestHandler.cs
+++ b/windows/TweetImpl.CefSharp/Handlers/CefResourceRequestHandler.cs
@@ -1,10 +1,11 @@
 using CefSharp;
 using CefSharp.Handler;
+using TweetImpl.CefSharp.Adapters;
 using TweetLib.Browser.CEF.Data;
 using TweetLib.Browser.CEF.Logic;
 using IResourceRequestHandler = TweetLib.Browser.Interfaces.IResourceRequestHandler;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Handlers {
 	sealed class CefResourceRequestHandler : ResourceRequestHandler {
 		private readonly ResourceRequestHandlerLogic<IRequest, IResponse, IResourceHandler> logic;
 
diff --git a/windows/TweetDuck/Browser/Base/CefResourceRequestHandlerFactory.cs b/windows/TweetImpl.CefSharp/Handlers/CefResourceRequestHandlerFactory.cs
similarity index 73%
rename from windows/TweetDuck/Browser/Base/CefResourceRequestHandlerFactory.cs
rename to windows/TweetImpl.CefSharp/Handlers/CefResourceRequestHandlerFactory.cs
index c211d7d8..11701a8a 100644
--- a/windows/TweetDuck/Browser/Base/CefResourceRequestHandlerFactory.cs
+++ b/windows/TweetImpl.CefSharp/Handlers/CefResourceRequestHandlerFactory.cs
@@ -1,10 +1,11 @@
 using System.Diagnostics.CodeAnalysis;
 using CefSharp;
+using TweetImpl.CefSharp.Adapters;
 using TweetLib.Browser.CEF.Data;
 using TweetLib.Browser.CEF.Logic;
 using IResourceRequestHandler = TweetLib.Browser.Interfaces.IResourceRequestHandler;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Handlers {
 	sealed class CefResourceRequestHandlerFactory : IResourceRequestHandlerFactory {
 		bool IResourceRequestHandlerFactory.HasHandlers => true;
 
@@ -15,7 +16,7 @@ public CefResourceRequestHandlerFactory(IResourceRequestHandler resourceRequestH
 		}
 
 		[SuppressMessage("ReSharper", "RedundantAssignment")]
-		CefSharp.IResourceRequestHandler IResourceRequestHandlerFactory.GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling) {
+		global::CefSharp.IResourceRequestHandler IResourceRequestHandlerFactory.GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling) {
 			return logic.GetResourceRequestHandler(request, ref disableDefaultHandling);
 		}
 	}
diff --git a/windows/TweetDuck/Browser/Base/CefResponseFilter.cs b/windows/TweetImpl.CefSharp/Handlers/CefResponseFilter.cs
similarity index 95%
rename from windows/TweetDuck/Browser/Base/CefResponseFilter.cs
rename to windows/TweetImpl.CefSharp/Handlers/CefResponseFilter.cs
index 466359b4..fc6f2c24 100644
--- a/windows/TweetDuck/Browser/Base/CefResponseFilter.cs
+++ b/windows/TweetImpl.CefSharp/Handlers/CefResponseFilter.cs
@@ -3,7 +3,7 @@
 using CefSharp;
 using TweetLib.Browser.CEF.Logic;
 
-namespace TweetDuck.Browser.Base {
+namespace TweetImpl.CefSharp.Handlers {
 	sealed class CefResponseFilter : IResponseFilter {
 		public static CefResponseFilter Create(ResponseFilterLogic logic) {
 			return logic == null ? null : new CefResponseFilter(logic);
diff --git a/windows/TweetDuck/Browser/Base/CefSchemeHandlerFactory.cs b/windows/TweetImpl.CefSharp/Handlers/CefSchemeHandlerFactory.cs
similarity index 86%
rename from windows/TweetDuck/Browser/Base/CefSchemeHandlerFactory.cs
rename to windows/TweetImpl.CefSharp/Handlers/CefSchemeHandlerFactory.cs
index 322fe6e8..5010efc8 100644
--- a/windows/TweetDuck/Browser/Base/CefSchemeHandlerFactory.cs
+++ b/windows/TweetImpl.CefSharp/Handlers/CefSchemeHandlerFactory.cs
@@ -1,10 +1,11 @@
 using CefSharp;
 using CefSharp.WinForms;
+using TweetImpl.CefSharp.Adapters;
 using TweetLib.Browser.CEF.Logic;
 using TweetLib.Browser.Interfaces;
 
-namespace TweetDuck.Browser.Base {
-	sealed class CefSchemeHandlerFactory : ISchemeHandlerFactory {
+namespace TweetImpl.CefSharp.Handlers {
+	public sealed class CefSchemeHandlerFactory : ISchemeHandlerFactory {
 		public static void Register(CefSettings settings, ICustomSchemeHandler handler) {
 			settings.RegisterScheme(new CefCustomScheme {
 				SchemeName = handler.Protocol,
diff --git a/windows/TweetImpl.CefSharp/Properties/AssemblyInfo.cs b/windows/TweetImpl.CefSharp/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..3650aa74
--- /dev/null
+++ b/windows/TweetImpl.CefSharp/Properties/AssemblyInfo.cs
@@ -0,0 +1,22 @@
+using System.Reflection;
+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("TweetDuck CefSharp Implementation")]
+[assembly: AssemblyDescription("TweetDuck CefSharp Implementation")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TweetImpl.CefSharp")]
+[assembly: AssemblyCopyright("")]
+[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("c97d4cf0-0bf2-4ec2-b450-dacd6bea56d8")]
diff --git a/windows/TweetImpl.CefSharp/TweetImpl.CefSharp.csproj b/windows/TweetImpl.CefSharp/TweetImpl.CefSharp.csproj
new file mode 100644
index 00000000..27b50baf
--- /dev/null
+++ b/windows/TweetImpl.CefSharp/TweetImpl.CefSharp.csproj
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\..\packages\CefSharp.Common.96.0.180\build\CefSharp.Common.props" Condition="Exists('..\..\packages\CefSharp.Common.96.0.180\build\CefSharp.Common.props')" />
+  <Import Project="..\..\packages\cef.redist.x86.96.0.18\build\cef.redist.x86.props" Condition="Exists('..\..\packages\cef.redist.x86.96.0.18\build\cef.redist.x86.props')" />
+  <Import Project="..\..\packages\cef.redist.x64.96.0.18\build\cef.redist.x64.props" Condition="Exists('..\..\packages\cef.redist.x64.96.0.18\build\cef.redist.x64.props')" />
+  <Import Project="..\..\packages\CefSharp.WinForms.92.0.260\build\CefSharp.WinForms.props" Condition="Exists('..\..\packages\CefSharp.WinForms.92.0.260\build\CefSharp.WinForms.props')" />
+  <Import Project="..\..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" />
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <ProjectGuid>{44DF3E2E-F465-4A31-8B43-F40FFFB018BA}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>TweetImpl.CefSharp</RootNamespace>
+    <AssemblyName>TweetImpl.CefSharp</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <LangVersion>8.0</LangVersion>
+    <FileAlignment>512</FileAlignment>
+    <UseVSHostingProcess>false</UseVSHostingProcess>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\x86\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <OutputPath>bin\x86\Release\</OutputPath>
+    <Optimize>true</Optimize>
+    <PlatformTarget>x86</PlatformTarget>
+    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="CefSharp, Version=96.0.180.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138">
+      <HintPath>..\..\packages\CefSharp.Common.96.0.180\lib\net452\CefSharp.dll</HintPath>
+    </Reference>
+    <Reference Include="CefSharp.Core, Version=96.0.180.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138">
+      <HintPath>..\..\packages\CefSharp.Common.96.0.180\lib\net452\CefSharp.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="CefSharp.WinForms, Version=96.0.180.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138">
+      <HintPath>..\..\packages\CefSharp.WinForms.96.0.180\lib\net462\CefSharp.WinForms.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Windows.Forms" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\lib\TweetLib.Browser.CEF\TweetLib.Browser.CEF.csproj">
+      <Project>{1b7793c6-9002-483e-9bd7-897fe6cd18fb}</Project>
+      <Name>TweetLib.Browser.CEF</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\lib\TweetLib.Browser\TweetLib.Browser.csproj">
+      <Project>{eefb1f37-7cad-46bd-8042-66e7b502ab02}</Project>
+      <Name>TweetLib.Browser</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\..\Version.cs" Link="Version.cs" />
+    <Compile Include="Adapters\CefAdapter.cs" />
+    <Compile Include="Adapters\CefBrowserAdapter.cs" />
+    <Compile Include="Adapters\CefDragDataAdapter.cs" />
+    <Compile Include="Adapters\CefErrorCodeAdapter.cs" />
+    <Compile Include="Adapters\CefFrameAdapter.cs" />
+    <Compile Include="Adapters\CefMenuModelAdapter.cs" />
+    <Compile Include="Adapters\CefRequestAdapter.cs" />
+    <Compile Include="Adapters\CefResponseAdapter.cs" />
+    <Compile Include="Component\BrowserComponentBase.cs" />
+    <Compile Include="Handlers\CefByteArrayResourceHandler.cs" />
+    <Compile Include="Handlers\CefContextMenuHandler.cs" />
+    <Compile Include="Handlers\CefDownloadRequestClient.cs" />
+    <Compile Include="Handlers\CefDragHandler.cs" />
+    <Compile Include="Handlers\CefLifeSpanHandler.cs" />
+    <Compile Include="Handlers\CefRequestHandler.cs" />
+    <Compile Include="Handlers\CefResourceHandlerFactory.cs" />
+    <Compile Include="Handlers\CefResourceRequestHandler.cs" />
+    <Compile Include="Handlers\CefResourceRequestHandlerFactory.cs" />
+    <Compile Include="Handlers\CefResponseFilter.cs" />
+    <Compile Include="Handlers\CefSchemeHandlerFactory.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props'))" />
+    <Error Condition="!Exists('..\..\packages\cef.redist.x64.96.0.18\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\cef.redist.x64.96.0.18\build\cef.redist.x64.props'))" />
+    <Error Condition="!Exists('..\..\packages\cef.redist.x86.96.0.18\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\cef.redist.x86.96.0.18\build\cef.redist.x86.props'))" />
+    <Error Condition="!Exists('..\..\packages\CefSharp.Common.96.0.180\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\CefSharp.Common.96.0.180\build\CefSharp.Common.props'))" />
+    <Error Condition="!Exists('..\..\packages\CefSharp.Common.96.0.180\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\CefSharp.Common.96.0.180\build\CefSharp.Common.targets'))" />
+  </Target>
+  <Import Project="..\..\packages\CefSharp.Common.96.0.180\build\CefSharp.Common.targets" Condition="Exists('..\..\packages\CefSharp.Common.96.0.180\build\CefSharp.Common.targets')" />
+</Project>
diff --git a/windows/TweetImpl.CefSharp/packages.config b/windows/TweetImpl.CefSharp/packages.config
new file mode 100644
index 00000000..f0120b1d
--- /dev/null
+++ b/windows/TweetImpl.CefSharp/packages.config
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="cef.redist.x64" version="96.0.18" targetFramework="net472" />
+  <package id="cef.redist.x86" version="96.0.18" targetFramework="net472" />
+  <package id="CefSharp.Common" version="96.0.180" targetFramework="net472" />
+  <package id="CefSharp.WinForms" version="96.0.180" targetFramework="net472" />
+  <package id="Microsoft.Net.Compilers" version="3.0.0" targetFramework="net472" developmentDependency="true" />
+</packages>