diff --git a/README.md b/README.md index 292b5e3..706f52b 100644 --- a/README.md +++ b/README.md @@ -5,21 +5,44 @@ SharpBrowser is the fastest open source C# web browser there is! Slightly faster ## Features - HTML5, CSS3, JS, HTML5 Video, WebGL 3D, etc -- Tabbed browsing (open in new tab, etc) -- Address bar (also opens Google Search) +- Tabbed browsing +- Address bar (also opens Google) - Back, Forward, Stop, Refresh - Developer tools -- Downloads window (download progress, cancellation, etc) +- Search bar (also highlights all instances) +- Download manager +- Custom error pages - Custom context menu -- Easy to add any vendor-specific buttons or commands +- Easily add vendor-specific branding, buttons or hotkeys +- View online & offline webpages + +## Hotkeys + +Hotkeys | Function +------------ | ------------- +Ctrl+T | Add a new tab +Ctrl+N | Add a new window +Ctrl+W | Close active tab +F5 | Refresh active tab +F12 | Open developer tools +Ctrl+Tab | Switch to the next tab +Ctrl+Shift+Tab | Switch to the previous tab +Ctrl+F | Open search bar (Enter to find next, Esc to close) ## Code +- SharpBrowser uses CefSharp 51, NET Framework 4.5.2 - `MainForm.cs` - main web browser UI and related functionality - `Handlers` - various handlers that we have registered with CefSharp that enable deeper integration between us and CefSharp - `Data/JSON.cs` - fast JSON serializer/deserializer - `bin` - Binaries are included in the `bin` folder due to the complex CefSharp setup required. Don't empty this folder. -- `bin/storage` - JS code and associated assets required for "downloads" page +- `bin/storage` - HTML and JS required for downloads manager and custom error pages + +## Credits + +- [Robin Rodricks](https://github.com/robinrodricks) - SharpBrowser project. +- [Alex Maitland](https://github.com/amaitland) - CefSharp project, wrapper for CEF embeddable browser. +- [Ahmet Uzun](https://github.com/postacik) - Original browser project. ## Screenshots @@ -31,10 +54,21 @@ SharpBrowser is the fastest open source C# web browser there is! Slightly faster ![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/2.png) +### Search Bar + +![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/search.png) + ### Downloads Tab ![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/3.png) ### Developer Tools -![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/4.png) \ No newline at end of file +![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/4.png) + +### Custom Error Pages + +![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/error1.png) + +![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/error2.png) + diff --git a/images/error1.png b/images/error1.png new file mode 100644 index 0000000..72ec9ce Binary files /dev/null and b/images/error1.png differ diff --git a/images/error2.png b/images/error2.png new file mode 100644 index 0000000..568bb12 Binary files /dev/null and b/images/error2.png differ diff --git a/images/search.png b/images/search.png new file mode 100644 index 0000000..5759e85 Binary files /dev/null and b/images/search.png differ diff --git a/src/App.config b/src/App.config index 5a2a13e..99fb1a1 100644 --- a/src/App.config +++ b/src/App.config @@ -7,4 +7,4 @@ - + diff --git a/src/Handlers/ContextMenuHandler.cs b/src/Handlers/ContextMenuHandler.cs new file mode 100644 index 0000000..3ec01ff --- /dev/null +++ b/src/Handlers/ContextMenuHandler.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using CefSharp; +using System.Windows.Forms; +using CefSharp.WinForms; + +namespace SharpBrowser { + internal class ContextMenuHandler : IContextMenuHandler { + + private const int ShowDevTools = 26501; + private const int CloseDevTools = 26502; + private const int SaveImageAs = 26503; + private const int SaveAsPdf = 26504; + private const int SaveLinkAs = 26505; + private const int CopyLinkAddress = 26506; + private const int OpenLinkInNewTab = 26507; + private const int CloseTab = 40007; + private const int RefreshTab = 40008; + MainForm myForm; + + private string lastSelText = ""; + + public ContextMenuHandler(MainForm form) { + myForm = form; + } + + public void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model) { + + // clear the menu + model.Clear(); + + // save text + lastSelText = parameters.SelectionText; + + // to copy text + if (parameters.SelectionText.CheckIfValid()) { + model.AddItem(CefMenuCommand.Copy, "Copy"); + model.AddSeparator(); + } + + //Removing existing menu item + //bool removed = model.Remove(CefMenuCommand.ViewSource); // Remove "View Source" option + if (parameters.LinkUrl != "") { + model.AddItem((CefMenuCommand)OpenLinkInNewTab, "Open link in new tab"); + model.AddItem((CefMenuCommand)CopyLinkAddress, "Copy link"); + model.AddSeparator(); + } + + if (parameters.HasImageContents && parameters.SourceUrl.CheckIfValid()) { + + // RIGHT CLICKED ON IMAGE + + } + + if (parameters.SelectionText != null) { + + // TEXT IS SELECTED + + } + + //Add new custom menu items + //#if DEBUG + model.AddItem((CefMenuCommand)ShowDevTools, "Developer tools"); + model.AddItem(CefMenuCommand.ViewSource, "View source"); + model.AddSeparator(); + //#endif + + model.AddItem((CefMenuCommand)RefreshTab, "Refresh tab"); + model.AddItem((CefMenuCommand)CloseTab, "Close tab"); + + } + + public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags) { + + int id = (int)commandId; + + if (id == ShowDevTools) { + browser.ShowDevTools(); + } + if (id == CloseDevTools) { + browser.CloseDevTools(); + } + if (id == SaveImageAs) { + browser.GetHost().StartDownload(parameters.SourceUrl); + } + if (id == SaveLinkAs) { + browser.GetHost().StartDownload(parameters.LinkUrl); + } + if (id == OpenLinkInNewTab) { + ChromiumWebBrowser newBrowser = myForm.AddNewBrowserTab(parameters.LinkUrl, false, browser.MainFrame.Url); + } + if (id == CopyLinkAddress) { + Clipboard.SetText(parameters.LinkUrl); + } + if (id == CloseTab) { + myForm.InvokeOnParent(delegate() { + myForm.CloseActiveTab(); + }); + } + if (id == RefreshTab) { + myForm.InvokeOnParent(delegate() { + myForm.RefreshActiveTab(); + }); + } + + return false; + } + + public void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame) { + + } + + public bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback) { + + // show default menu + return false; + } + } +} diff --git a/src/Handlers/DownloadHandler.cs b/src/Handlers/DownloadHandler.cs index a00e616..853e824 100644 --- a/src/Handlers/DownloadHandler.cs +++ b/src/Handlers/DownloadHandler.cs @@ -1,8 +1,7 @@ using CefSharp; -namespace SharpBrowser -{ - public class DownloadHandler : IDownloadHandler +namespace SharpBrowser { + internal class DownloadHandler : IDownloadHandler { MainForm myForm; @@ -11,14 +10,34 @@ public DownloadHandler(MainForm form) myForm = form; } - public void OnBeforeDownload(IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) + public void OnBeforeDownload(IBrowser browser, DownloadItem item, IBeforeDownloadCallback callback) { if (!callback.IsDisposed) { using (callback) { - myForm.UpdateDownloadItem(downloadItem); - callback.Continue(downloadItem.SuggestedFileName, showDialog: true); + + myForm.UpdateDownloadItem(item); + + // ask browser what path it wants to save the file into + string path = myForm.CalcDownloadPath(item); + + // if file should not be saved, path will be null, so skip file + if (path != null) { + + // skip file + callback.Continue(path, false); + + } else { + + // download file + callback.Dispose(); + + // open the downloads tab + myForm.OpenDownloadsTab(); + + } + } } } @@ -26,7 +45,9 @@ public void OnBeforeDownload(IBrowser browser, DownloadItem downloadItem, IBefor public void OnDownloadUpdated(IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback) { myForm.UpdateDownloadItem(downloadItem); - if (downloadItem.IsInProgress && myForm.CancelRequests.Contains(downloadItem.Id)) callback.Cancel(); + if (downloadItem.IsInProgress && myForm.CancelRequests.Contains(downloadItem.Id)) { + callback.Cancel(); + } //Console.WriteLine(downloadItem.Url + " %" + downloadItem.PercentComplete + " complete"); } } diff --git a/src/Handlers/HostHandler.cs b/src/Handlers/HostHandler.cs index f1cc14c..28433bf 100644 --- a/src/Handlers/HostHandler.cs +++ b/src/Handlers/HostHandler.cs @@ -9,7 +9,7 @@ namespace SharpBrowser { /// /// functions in this class are accessible by JS using the code `host.X()` /// - public class HostHandler { + internal class HostHandler { MainForm myForm; public HostHandler(MainForm form) { @@ -34,6 +34,9 @@ public bool cancelDownload(int downloadId) { } return true; } + public void refreshActiveTab() { + myForm.RefreshActiveTab(); + } } } diff --git a/src/Handlers/IconHelper.cs b/src/Handlers/IconHelper.cs deleted file mode 100644 index 881d2b3..0000000 --- a/src/Handlers/IconHelper.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace SharpBrowser -{ - /// - /// Provides static methods to read system icons for both folders and files. - /// - /// - /// IconReader.GetFileIcon("c:\\general.xls"); - /// - public class IconReader - { - /// - /// Options to specify the size of icons to return. - /// - public enum IconSize - { - /// - /// Specify large icon - 32 pixels by 32 pixels. - /// - Large = 0, - /// - /// Specify small icon - 16 pixels by 16 pixels. - /// - Small = 1 - } - - /// - /// Options to specify whether folders should be in the open or closed state. - /// - public enum FolderType - { - /// - /// Specify open folder. - /// - Open = 0, - /// - /// Specify closed folder. - /// - Closed = 1 - } - - /// - /// Returns an icon for a given file - indicated by the name parameter. - /// - /// Pathname for file. - /// Large or small - /// Whether to include the link icon - /// System.Drawing.Icon - public static System.Drawing.Icon GetFileIcon(string name, IconSize size, bool linkOverlay) - { - Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO(); - uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES; - - if (true == linkOverlay) flags += Shell32.SHGFI_LINKOVERLAY; - - /* Check the size specified for return. */ - if (IconSize.Small == size) - { - flags += Shell32.SHGFI_SMALLICON ; - } - else - { - flags += Shell32.SHGFI_LARGEICON ; - } - - Shell32.SHGetFileInfo( name, - Shell32.FILE_ATTRIBUTE_NORMAL, - ref shfi, - (uint) System.Runtime.InteropServices.Marshal.SizeOf(shfi), - flags ); - - // Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly - System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone(); - User32.DestroyIcon( shfi.hIcon ); // Cleanup - return icon; - } - - /// - /// Used to access system folder icons. - /// - /// Specify large or small icons. - /// Specify open or closed FolderType. - /// System.Drawing.Icon - public static System.Drawing.Icon GetFolderIcon( IconSize size, FolderType folderType ) - { - // Need to add size check, although errors generated at present! - uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES; - - if (FolderType.Open == folderType) - { - flags += Shell32.SHGFI_OPENICON; - } - - if (IconSize.Small == size) - { - flags += Shell32.SHGFI_SMALLICON; - } - else - { - flags += Shell32.SHGFI_LARGEICON; - } - - // Get the folder icon - Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO(); - Shell32.SHGetFileInfo( null, - Shell32.FILE_ATTRIBUTE_DIRECTORY, - ref shfi, - (uint) System.Runtime.InteropServices.Marshal.SizeOf(shfi), - flags ); - - System.Drawing.Icon.FromHandle(shfi.hIcon); // Load the icon from an HICON handle - - // Now clone the icon, so that it can be successfully stored in an ImageList - System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone(); - - User32.DestroyIcon( shfi.hIcon ); // Cleanup - return icon; - } - } - - /// - /// Wraps necessary Shell32.dll structures and functions required to retrieve Icon Handles using SHGetFileInfo. Code - /// courtesy of MSDN Cold Rooster Consulting case study. - /// - /// - - // This code has been left largely untouched from that in the CRC example. The main changes have been moving - // the icon reading code over to the IconReader type. - public class Shell32 - { - - public const int MAX_PATH = 256; - [StructLayout(LayoutKind.Sequential)] - public struct SHITEMID - { - public ushort cb; - [MarshalAs(UnmanagedType.LPArray)] - public byte[] abID; - } - - [StructLayout(LayoutKind.Sequential)] - public struct ITEMIDLIST - { - public SHITEMID mkid; - } - - [StructLayout(LayoutKind.Sequential)] - public struct BROWSEINFO - { - public IntPtr hwndOwner; - public IntPtr pidlRoot; - public IntPtr pszDisplayName; - [MarshalAs(UnmanagedType.LPTStr)] - public string lpszTitle; - public uint ulFlags; - public IntPtr lpfn; - public int lParam; - public IntPtr iImage; - } - - // Browsing for directory. - public const uint BIF_RETURNONLYFSDIRS = 0x0001; - public const uint BIF_DONTGOBELOWDOMAIN = 0x0002; - public const uint BIF_STATUSTEXT = 0x0004; - public const uint BIF_RETURNFSANCESTORS = 0x0008; - public const uint BIF_EDITBOX = 0x0010; - public const uint BIF_VALIDATE = 0x0020; - public const uint BIF_NEWDIALOGSTYLE = 0x0040; - public const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX); - public const uint BIF_BROWSEINCLUDEURLS = 0x0080; - public const uint BIF_BROWSEFORCOMPUTER = 0x1000; - public const uint BIF_BROWSEFORPRINTER = 0x2000; - public const uint BIF_BROWSEINCLUDEFILES = 0x4000; - public const uint BIF_SHAREABLE = 0x8000; - - [StructLayout(LayoutKind.Sequential)] - public struct SHFILEINFO - { - public const int NAMESIZE = 80; - public IntPtr hIcon; - public int iIcon; - public uint dwAttributes; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_PATH)] - public string szDisplayName; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst=NAMESIZE)] - public string szTypeName; - }; - - public const uint SHGFI_ICON = 0x000000100; // get icon - public const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name - public const uint SHGFI_TYPENAME = 0x000000400; // get type name - public const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes - public const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location - public const uint SHGFI_EXETYPE = 0x000002000; // return exe type - public const uint SHGFI_SYSICONINDEX = 0x000004000; // get system icon index - public const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon - public const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state - public const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes - public const uint SHGFI_LARGEICON = 0x000000000; // get large icon - public const uint SHGFI_SMALLICON = 0x000000001; // get small icon - public const uint SHGFI_OPENICON = 0x000000002; // get open icon - public const uint SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon - public const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl - public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute - public const uint SHGFI_ADDOVERLAYS = 0x000000020; // apply the appropriate overlays - public const uint SHGFI_OVERLAYINDEX = 0x000000040; // Get the index of the overlay - - public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010; - public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080; - - [DllImport("Shell32.dll")] - public static extern IntPtr SHGetFileInfo( - string pszPath, - uint dwFileAttributes, - ref SHFILEINFO psfi, - uint cbFileInfo, - uint uFlags - ); - } - - /// - /// Wraps necessary functions imported from User32.dll. Code courtesy of MSDN Cold Rooster Consulting example. - /// - public class User32 - { - /// - /// Provides access to function required to delete handle. This method is used internally - /// and is not required to be called separately. - /// - /// Pointer to icon handle. - /// N/A - [DllImport("User32.dll")] - public static extern int DestroyIcon( IntPtr hIcon ); - } -} - diff --git a/src/Handlers/KeyboardHandler.cs b/src/Handlers/KeyboardHandler.cs index 39f5d08..4a4ed95 100644 --- a/src/Handlers/KeyboardHandler.cs +++ b/src/Handlers/KeyboardHandler.cs @@ -1,13 +1,20 @@ -using System.Windows.Forms; + using System; +using System.Collections.Generic; +using System.Windows.Forms; using CefSharp; -namespace SharpBrowser -{ - public class KeyboardHandler : IKeyboardHandler +namespace SharpBrowser { + internal class KeyboardHandler : IKeyboardHandler { MainForm myForm; + public static List Hotkeys = new List(); + public static void AddHotKey(Form form, Action function, Keys key, bool ctrl = false, bool shift = false, bool alt = false) { + Utils.AddHotKey(form, function, key, ctrl, shift, alt); + Hotkeys.Add(new SharpHotKey(function, key, ctrl, shift, alt)); + } + public KeyboardHandler(MainForm form) { myForm = form; @@ -17,17 +24,34 @@ public bool OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType return false; } - /// > - public bool OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey) - { - if (type == KeyType.KeyUp && windowsKeyCode == (int)Keys.F4 && (modifiers == CefEventFlags.ControlDown)) - { - //Debug.WriteLine("Ctrl-F4"); - myForm.CloseActiveTab(); - } + /// + public bool OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey) { + + if (type == KeyType.RawKeyDown) { - //Debug.WriteLine(String.Format("OnKeyEvent: KeyType: {0} 0x{1:X} Modifiers: {2}", type, windowsKeyCode, modifiers)); - return false; - } + + // check if my hotkey + int mod = ((int)modifiers); + bool ctrlDown = mod.IsBitmaskOn((int)CefEventFlags.ControlDown); + bool shiftDown = mod.IsBitmaskOn((int)CefEventFlags.ShiftDown); + bool altDown = mod.IsBitmaskOn((int)CefEventFlags.AltDown); + + // per registered hotkey + foreach (SharpHotKey key in Hotkeys) { + if (key.KeyCode == windowsKeyCode){ + if (key.Ctrl == ctrlDown && key.Shift == shiftDown && key.Alt == altDown) { + myForm.InvokeOnParent(delegate() { + key.Callback(); + }); + } + } + } + + //Debug.WriteLine(String.Format("OnKeyEvent: KeyType: {0} 0x{1:X} Modifiers: {2}", type, windowsKeyCode, modifiers)); + + } + + return false; + } } } diff --git a/src/Handlers/LifeSpanHandler.cs b/src/Handlers/LifeSpanHandler.cs index daec080..00829a9 100644 --- a/src/Handlers/LifeSpanHandler.cs +++ b/src/Handlers/LifeSpanHandler.cs @@ -1,8 +1,7 @@ using CefSharp; -namespace SharpBrowser -{ - public class LifeSpanHandler : ILifeSpanHandler +namespace SharpBrowser { + internal class LifeSpanHandler : ILifeSpanHandler { MainForm myForm; @@ -11,25 +10,151 @@ public LifeSpanHandler(MainForm form) myForm = form; } - bool ILifeSpanHandler.OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IWindowInfo windowInfo, ref bool noJavascriptAccess, out IWebBrowser newBrowser) - { - newBrowser = myForm.AddNewBrowserTab(targetUrl); - return true; - } - void ILifeSpanHandler.OnAfterCreated(IWebBrowser browserControl, IBrowser browser) - { + // Summary: + // Called when a browser has recieved a request to close. This may result directly + // from a call to CefBrowserHost::CloseBrowser() or indirectly if the browser + // is a top-level OS window created by CEF and the user attempts to close the + // window. This method will be called after the JavaScript 'onunload' event + // has been fired. It will not be called for browsers after the associated OS + // window has been destroyed (for those browsers it is no longer possible to + // cancel the close). If CEF created an OS window for the browser returning + // false will send an OS close notification to the browser window's top-level + // owner (e.g. WM_CLOSE on Windows, performClose: on OS-X and "delete_event" + // on Linux). If no OS window exists (window rendering disabled) returning false + // will cause the browser object to be destroyed immediately. Return true if + // the browser is parented to another window and that other window needs to + // receive close notification via some non-standard technique. If an application + // provides its own top-level window it should handle OS close notifications + // by calling CefBrowserHost::CloseBrowser(false) instead of immediately closing + // (see the example below). This gives CEF an opportunity to process the 'onbeforeunload' + // event and optionally cancel the close before DoClose() is called. The CefLifeSpanHandler::OnBeforeClose() + // method will be called immediately before the browser object is destroyed. + // The application should only exit after OnBeforeClose() has been called for + // all existing browsers. If the browser represents a modal window and a custom + // modal loop implementation was provided in CefLifeSpanHandler::RunModal() + // this callback should be used to restore the opener window to a usable state. + // By way of example consider what should happen during window close when the + // browser is parented to an application-provided top-level OS window. 1. User + // clicks the window close button which sends an OS close notification (e.g. + // WM_CLOSE on Windows, performClose: on OS-X and "delete_event" on Linux). + // 2. Application's top-level window receives the close notification and: A. + // Calls CefBrowserHost::CloseBrowser(false). B. Cancels the window close. + // 3. JavaScript 'onbeforeunload' handler executes and shows the close confirmation + // dialog (which can be overridden via CefJSDialogHandler::OnBeforeUnloadDialog()). + // 4. User approves the close. 5. JavaScript 'onunload' handler executes. + // 6. Application's DoClose() handler is called. Application will: A. Set a + // flag to indicate that the next close attempt will be allowed. B. Return + // false. 7. CEF sends an OS close notification. 8. Application's top-level + // window receives the OS close notification and allows the window to close + // based on the flag from #6B. 9. Browser OS window is destroyed. 10. Application's + // CefLifeSpanHandler::OnBeforeClose() handler is called and the browser object + // is destroyed. 11. Application exits by calling CefQuitMessageLoop() if no + // other browsers exist. + // + // Parameters: + // browserControl: + // The CefSharp.IWebBrowser control that is realted to the window is closing. + // + // browser: + // The browser instance + // + // Returns: + // For default behaviour return false + public bool DoClose(IWebBrowser browserControl, IBrowser browser) { + return false; + } + // + // Summary: + // Called after a new browser is created. + // + // Parameters: + // browserControl: + // The CefSharp.IWebBrowser control that is realted to the window is closing. + // + // browser: + // The browser instance + public void OnAfterCreated(IWebBrowser browserControl, IBrowser browser) { + } + // + // Summary: + // Called before a CefBrowser window (either the main browser for CefSharp.IWebBrowser, + // or one of its children) + // + // Parameters: + // browserControl: + // The CefSharp.IWebBrowser control that is realted to the window is closing. + // + // browser: + // The browser instance + public void OnBeforeClose(IWebBrowser browserControl, IBrowser browser) { + } + // + // Summary: + // Called before a popup window is created. + // + // Parameters: + // browserControl: + // The CefSharp.IWebBrowser control this request is for. + // + // browser: + // The browser instance that launched this popup. + // + // frame: + // The HTML frame that launched this popup. + // + // targetUrl: + // The URL of the popup content. (This may be empty/null) + // + // targetFrameName: + // The name of the popup. (This may be empty/null) + // + // targetDisposition: + // The value indicates where the user intended to open the popup (e.g. current + // tab, new tab, etc) + // + // userGesture: + // The value will be true if the popup was opened via explicit user gesture + // (e.g. clicking a link) or false if the popup opened automatically (e.g. via + // the DomContentLoaded event). + // + // popupFeatures: + // structure contains additional information about the requested popup window + // + // windowInfo: + // window information + // + // browserSettings: + // browser settings, defaults to source browsers + // + // noJavascriptAccess: + // value indicates whether the new browser window should be scriptable and in + // the same process as the source browser. + // + // newBrowser: + // EXPERIMENTAL - A newly created browser that will host the popup + // + // Returns: + // To cancel creation of the popup window return true otherwise return false. + // + // Remarks: + // CEF documentation: Called on the IO thread before a new popup window is created. + // The |browser| and |frame| parameters represent the source of the popup request. + // The |target_url| and |target_frame_name| values may be empty if none were + // specified with the request. The |popupFeatures| structure contains information + // about the requested popup window. To allow creation of the popup window optionally + // modify |windowInfo|, |client|, |settings| and |no_javascript_access| and + // return false. To cancel creation of the popup window return true. The |client| + // and |settings| values will default to the source browser's values. The |no_javascript_access| + // value indicates whether the new browser window should be scriptable and in + // the same process as the source browser. + public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser) { - } + // open popup in new tab! + newBrowser = myForm.AddNewBrowserTab(targetUrl); - bool ILifeSpanHandler.DoClose(IWebBrowser browserControl, IBrowser browser) - { - return false; - } - - public void OnBeforeClose(IWebBrowser browserControl, IBrowser browser) - { + return true; - } + } } } diff --git a/src/Handlers/MenuHandler.cs b/src/Handlers/MenuHandler.cs deleted file mode 100644 index 603f52e..0000000 --- a/src/Handlers/MenuHandler.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using CefSharp; -using System.Windows.Forms; -using CefSharp.WinForms; - -namespace SharpBrowser -{ - internal class MenuHandler : IContextMenuHandler - { - private const int ShowDevTools = 26501; - private const int CloseDevTools = 26502; - private const int SaveImageAs = 26503; - private const int SaveAsPdf = 26504; - private const int SaveLinkAs = 26505; - private const int CopyLinkAddress = 26506; - private const int OpenLinkInNewTab = 26507; - MainForm myForm; - - public MenuHandler(MainForm form) - { - myForm = form; - } - - void IContextMenuHandler.OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model) - { - //To disable the menu then call clear - model.Clear(); - - //Removing existing menu item - //bool removed = model.Remove(CefMenuCommand.ViewSource); // Remove "View Source" option - if (parameters.LinkUrl != "") - { - model.AddItem((CefMenuCommand)OpenLinkInNewTab, "Open link in new tab"); - model.AddItem((CefMenuCommand)SaveLinkAs, "Save link as"); - model.AddItem((CefMenuCommand)CopyLinkAddress, "Copy link address"); - model.AddSeparator(); - } - - if (parameters.HasImageContents && parameters.SourceUrl != "") - { - model.AddItem((CefMenuCommand)SaveImageAs, "Save image as"); - model.AddSeparator(); - } - - // model.AddItem((CefMenuCommand)SaveAsPdf, "Save as Pdf"); - - //Add new custom menu items - model.AddItem((CefMenuCommand)ShowDevTools, "Developer tools"); - model.AddItem(CefMenuCommand.ViewSource, "View source"); - } - - bool IContextMenuHandler.OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags) - { - if ((int)commandId == ShowDevTools) - { - browser.ShowDevTools(); - } - if ((int)commandId == CloseDevTools) - { - browser.CloseDevTools(); - } - if ((int)commandId == SaveImageAs) - { - browser.GetHost().StartDownload(parameters.SourceUrl); - } - if ((int)commandId == SaveLinkAs) - { - browser.GetHost().StartDownload(parameters.LinkUrl); - } - if ((int)commandId == OpenLinkInNewTab) - { - ChromiumWebBrowser newBrowser = myForm.AddNewBrowserTab(parameters.LinkUrl, false); - } - if ((int)commandId == CopyLinkAddress) - { - Clipboard.SetText(parameters.LinkUrl); - } - - /* if ((int)commandId == SaveAsPdf) - { - PdfPrintSettings settings = new PdfPrintSettings(); - settings.Landscape = true; - settings.BackgroundsEnabled = false; - browser.PrintToPdfAsync(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\SharpBrowser.pdf", settings); - }*/ - - return false; - } - - void IContextMenuHandler.OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame) - { - - } - - /*bool IContextMenuHandler.RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model) - { - return false; - }*/ - } -} diff --git a/src/Handlers/RequestHandler.cs b/src/Handlers/RequestHandler.cs new file mode 100644 index 0000000..2338581 --- /dev/null +++ b/src/Handlers/RequestHandler.cs @@ -0,0 +1,336 @@ +using System.Collections.Specialized; +using CefSharp; + +namespace SharpBrowser { + internal class RequestHandler : IRequestHandler { + MainForm myForm; + + public RequestHandler(MainForm form) { + myForm = form; + } + + // Summary: + // Called when the browser needs credentials from the user. + // + // Parameters: + // frame: + // The frame object that needs credentials (This will contain the URL that is + // being requested.) + // + // isProxy: + // indicates whether the host is a proxy server + // + // callback: + // Callback interface used for asynchronous continuation of authentication requests. + // + // Returns: + // Return true to continue the request and call CefAuthCallback::Continue() + // when the authentication information is available. Return false to cancel + // the request. + public bool GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback) { + + return false; + } + // + // Summary: + // Called on the CEF IO thread to optionally filter resource response content. + // + // Parameters: + // frame: + // The frame that is being redirected. + // + // request: + // the request object - cannot be modified in this callback + // + // response: + // the response object - cannot be modified in this callback + // + // Returns: + // Return an IResponseFilter to intercept this response, otherwise return null + public IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response) { + return null; + } + // + // Summary: + // Called before browser navigation. If the navigation is allowed CefSharp.IWebBrowser.FrameLoadStart + // and CefSharp.IWebBrowser.FrameLoadEnd will be called. If the navigation is + // canceled CefSharp.IWebBrowser.LoadError will be called with an ErrorCode + // value of CefSharp.CefErrorCode.Aborted. + // + // Parameters: + // frame: + // The frame the request is coming from + // + // request: + // the request object - cannot be modified in this callback + // + // isRedirect: + // has the request been redirected + // + // Returns: + // Return true to cancel the navigation or false to allow the navigation to + // proceed. + public bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect) { + return false; + } + // + // Summary: + // Called before a resource request is loaded. For async processing return CefSharp.CefReturnValue.ContinueAsync + // and execute CefSharp.IRequestCallback.Continue(System.Boolean) or CefSharp.IRequestCallback.Cancel() + // + // Parameters: + // frame: + // The frame object + // + // request: + // the request object - can be modified in this callback. + // + // callback: + // Callback interface used for asynchronous continuation of url requests. + // + // Returns: + // To cancel loading of the resource return CefSharp.CefReturnValue.Cancel or + // CefSharp.CefReturnValue.Continue to allow the resource to load normally. + // For async return CefSharp.CefReturnValue.ContinueAsync + public CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback) { + + // if referer given + var tab = myForm.GetTabByBrowser(browserControl); + if (tab != null && tab.RefererURL != null) { + + // Set referer + request.SetReferrer(tab.RefererURL, ReferrerPolicy.Always); + + } + + return CefSharp.CefReturnValue.Continue; + } + // + // Summary: + // Called to handle requests for URLs with an invalid SSL certificate. Return + // true and call CefSharp.IRequestCallback.Continue(System.Boolean) either in + // this method or at a later time to continue or cancel the request. If CefSettings.IgnoreCertificateErrors + // is set all invalid certificates will be accepted without calling this method. + // + // Parameters: + // errorCode: + // the error code for this invalid certificate + // + // requestUrl: + // the url of the request for the invalid certificate + // + // sslInfo: + // ssl certificate information + // + // callback: + // Callback interface used for asynchronous continuation of url requests. If + // empty the error cannot be recovered from and the request will be canceled + // automatically. + // + // Returns: + // Return false to cancel the request immediately. Return true and use CefSharp.IRequestCallback + // to execute in an async fashion. + public bool OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback) { + return true; + } + // + // Summary: + // Called on the UI thread before OnBeforeBrowse in certain limited cases where + // navigating a new or different browser might be desirable. This includes user-initiated + // navigation that might open in a special way (e.g. links clicked via middle-click + // or ctrl + left-click) and certain types of cross-origin navigation initiated + // from the renderer process (e.g. navigating the top-level frame to/from a + // file URL). + // + // Parameters: + // frame: + // The frame object + // + // targetDisposition: + // The value indicates where the user intended to navigate the browser based + // on standard Chromium behaviors (e.g. current tab, new tab, etc). + // + // userGesture: + // The value will be true if the browser navigated via explicit user gesture + // (e.g. clicking a link) or false if it navigated automatically (e.g. via the + // DomContentLoaded event). + // + // Returns: + // Return true to cancel the navigation or false to allow the navigation to + // proceed in the source browser's top-level frame. + public bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture) { + return false; + } + // + // Summary: + // Called when a plugin has crashed + // + // Parameters: + // pluginPath: + // path of the plugin that crashed + public void OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath) { + } + // + // Summary: + // Called on the UI thread to handle requests for URLs with an unknown protocol + // component. SECURITY WARNING: YOU SHOULD USE THIS METHOD TO ENFORCE RESTRICTIONS + // BASED ON SCHEME, HOST OR OTHER URL ANALYSIS BEFORE ALLOWING OS EXECUTION. + // + // Parameters: + // url: + // the request url + // + // Returns: + // return to true to attempt execution via the registered OS protocol handler, + // if any. Otherwise return false. + public bool OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url) { + return true; + } + // + // Summary: + // Called when JavaScript requests a specific storage quota size via the webkitStorageInfo.requestQuota + // function. For async processing return true and execute CefSharp.IRequestCallback.Continue(System.Boolean) + // at a later time to grant or deny the request or CefSharp.IRequestCallback.Cancel() + // to cancel. + // + // Parameters: + // originUrl: + // the origin of the page making the request + // + // newSize: + // is the requested quota size in bytes + // + // callback: + // Callback interface used for asynchronous continuation of url requests. + // + // Returns: + // Return false to cancel the request immediately. Return true to continue the + // request and call CefSharp.IRequestCallback.Continue(System.Boolean) either + // in this method or at a later time to grant or deny the request. + public bool OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl, long newSize, IRequestCallback callback) { + callback.Continue(true); + return true; + } + // + // Summary: + // Called when the render process terminates unexpectedly. + // + // Parameters: + // status: + // indicates how the process terminated. + public void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status) { + } + // + // Summary: + // Called on the CEF UI thread when the render view associated with browser + // is ready to receive/handle IPC messages in the render process. + public void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser) { + } + // + // Summary: + // Called on the CEF IO thread when a resource load has completed. + // + // Parameters: + // frame: + // The frame that is being redirected. + // + // request: + // the request object - cannot be modified in this callback + // + // response: + // the response object - cannot be modified in this callback + // + // status: + // indicates the load completion status + // + // receivedContentLength: + // is the number of response bytes actually read. + public void OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength) { + } + // + // Summary: + // Called on the IO thread when a resource load is redirected. The CefSharp.IRequest.Url + // parameter will contain the old URL and other request-related information. + // + // Parameters: + // frame: + // The frame that is being redirected. + // + // request: + // the request object - cannot be modified in this callback + // + // newUrl: + // the new URL and can be changed if desired + public void OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, ref string newUrl) { + } + // + // Summary: + // Called on the CEF IO thread when a resource response is received. To allow + // the resource to load normally return false. To redirect or retry the resource + // modify request (url, headers or post body) and return true. The response + // object cannot be modified in this callback. + // + // Parameters: + // frame: + // The frame that is being redirected. + // + // request: + // the request object + // + // response: + // the response object - cannot be modified in this callback + // + // Returns: + // To allow the resource to load normally return false. To redirect or retry + // the resource modify request (url, headers or post body) and return true. + public bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response) { + + + int code = response.StatusCode; + + + // if NOT FOUND + if (code == 404) { + + if (!request.Url.IsURLLocalhost()) { + + // redirect to web archive to try and find older version + request.Url = "http://web.archive.org/web/*/" + request.Url; + + } else { + + // show offline "file not found" page + request.Url = MainForm.FileNotFoundURL + "?path=" + request.Url.EncodeURL(); + } + + return true; + } + + + // if FILE NOT FOUND + if (code == 0 && request.Url.IsURLOfflineFile()) { + string path = request.Url.FileURLToPath(); + if (path.FileNotExists()) { + + // show offline "file not found" page + request.Url = MainForm.FileNotFoundURL + "?path=" + path.EncodeURL(); + return true; + + } + } else { + + // if CANNOT CONNECT + if (code == 0 || code == 444 || (code >= 500 && code <= 599)) { + + // show offline "cannot connect to server" page + request.Url = MainForm.CannotConnectURL; + return true; + } + + } + + return false; + } + + } +} diff --git a/src/Handlers/SchemeHandler.cs b/src/Handlers/SchemeHandler.cs index 80ec5a6..2bb164a 100644 --- a/src/Handlers/SchemeHandler.cs +++ b/src/Handlers/SchemeHandler.cs @@ -8,96 +8,181 @@ using System.Windows.Forms; using System.Drawing; -namespace SharpBrowser -{ - internal class SchemeHandler : IResourceHandler +namespace SharpBrowser { + internal class SchemeHandler : IResourceHandler, IDisposable { - private static readonly IDictionary ResourceDictionary; private static string appPath = Path.GetDirectoryName(Application.ExecutablePath) + @"\"; private string mimeType; private Stream stream; - - static SchemeHandler() - { - ResourceDictionary = new Dictionary - { - { "/home.html", "" } - }; - } - - private MemoryStream GetFileIcon(string name, IconReader.IconSize size) - { - Icon icon = IconReader.GetFileIcon(name, size, false); - using (icon) - { - using (var bmp = icon.ToBitmap()) - { - MemoryStream ms = new MemoryStream(); - bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png); - ms.Seek(0, SeekOrigin.Begin); - return ms; - } - } - } - - - public bool ProcessRequestAsync(IRequest request, ICallback callback) - { - var uri = new Uri(request.Url); - var fileName = uri.AbsolutePath; - - if (uri.Host == "storage") - { - fileName = appPath + uri.Host + fileName; - if (File.Exists(fileName)) - { - Task.Factory.StartNew(() => - { - using (callback) - { - //var bytes = Encoding.UTF8.GetBytes(resource); - //stream = new MemoryStream(bytes); - FileStream fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); - mimeType = ResourceHandler.GetMimeType(Path.GetExtension(fileName)); - stream = fStream; - callback.Continue(); - } - }); - - return true; - } - } - - if (uri.Host == "fileicon") - { - Task.Factory.StartNew(() => - { - using (callback) - { - stream = GetFileIcon(fileName, IconReader.IconSize.Large); - mimeType = ResourceHandler.GetMimeType(".png"); - callback.Continue(); - } - }); - return true; - } - - - callback.Dispose(); - return false; - } - - public Stream GetResponse(IResponse response, out long responseLength, out string redirectUrl) - { - responseLength = stream.Length; - redirectUrl = null; - - response.StatusCode = (int)HttpStatusCode.OK; - response.StatusText = "OK"; - response.MimeType = mimeType; - - return stream; - } + MainForm myForm; + private Uri uri; + private string fileName; + + public SchemeHandler(MainForm form) { + myForm = form; + } + + public void Dispose() { + + } + + + // + // Summary: + // Begin processing the request. + // + // Parameters: + // request: + // The request object. + // + // callback: + // The callback used to Continue or Cancel the request (async). + // + // Returns: + // To handle the request return true and call CefSharp.ICallback.Continue() + // once the response header information is available CefSharp.ICallback.Continue() + // can also be called from inside this method if header information is available + // immediately). To cancel the request return false. + public bool ProcessRequest(IRequest request, ICallback callback) { + uri = new Uri(request.Url); + fileName = uri.AbsolutePath; + + // if url is blocked + /*if (!myForm.IsURLOk(request.Url)) { + + // return true so it does not open up + return true; + }*/ + + // if url is browser file + if (uri.Host == "storage") { + fileName = appPath + uri.Host + fileName; + if (File.Exists(fileName)) { + Task.Factory.StartNew(() => { + using (callback) { + //var bytes = Encoding.UTF8.GetBytes(resource); + //stream = new MemoryStream(bytes); + FileStream fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); + mimeType = ResourceHandler.GetMimeType(Path.GetExtension(fileName)); + stream = fStream; + callback.Continue(); + } + }); + + return true; + } + } + + // if url is request for icon of another file + if (uri.Host == "fileicon") { + Task.Factory.StartNew(() => { + using (callback) { + stream = FileIconUtils.GetFileIcon(fileName, FileIconSize.Large); + mimeType = ResourceHandler.GetMimeType(".png"); + callback.Continue(); + } + }); + return true; + } + + + // by default reject + callback.Dispose(); + return false; + } + // + // Summary: + // Retrieve response header information. If the response length is not known + // set responseLength to -1 and ReadResponse() will be called until it returns + // false. If the response length is known set responseLength to a positive value + // and ReadResponse() will be called until it returns false or the specified + // number of bytes have been read. If an error occured while setting up the + // request you can set CefSharp.IResponse.ErrorCode to indicate the error condition. + // + // Parameters: + // response: + // Use the response object to set the mime type, http status code and other + // optional header values. + // + // responseLength: + // If the response length is not known set responseLength to -1 + // + // redirectUrl: + // To redirect the request to a new URL set redirectUrl to the new Url. + public void GetResponseHeaders(IResponse response, out long responseLength, out string redirectUrl) { + + responseLength = stream.Length; + redirectUrl = null; + + response.StatusCode = (int)HttpStatusCode.OK; + response.StatusText = "OK"; + response.MimeType = mimeType; + + //return stream; + } + // + // Summary: + // Read response data. If data is available immediately copy to dataOut, set + // bytesRead to the number of bytes copied, and return true. To read the data + // at a later time set bytesRead to 0, return true and call ICallback.Continue() + // when the data is available. To indicate response completion return false. + // + // Parameters: + // dataOut: + // Stream to write to + // + // bytesRead: + // Number of bytes copied to the stream + // + // callback: + // The callback used to Continue or Cancel the request (async). + // + // Returns: + // If data is available immediately copy to dataOut, set bytesRead to the number + // of bytes copied, and return true.To indicate response completion return false. + // + // Remarks: + // Depending on this size of your response this method may be called multiple + // times + public bool ReadResponse(Stream dataOut, out int bytesRead, ICallback callback) { + + //Dispose the callback as it's an unmanaged resource, we don't need it in this case + callback.Dispose(); + + if (stream == null) { + bytesRead = 0; + return false; + } + + //Data out represents an underlying buffer (typically 32kb in size). + var buffer = new byte[dataOut.Length]; + bytesRead = stream.Read(buffer, 0, buffer.Length); + + dataOut.Write(buffer, 0, buffer.Length); + + return bytesRead > 0; + + } + // Summary: + // Request processing has been canceled. + public void Cancel() { + } + // + // Summary: + // Return true if the specified cookie can be sent with the request or false + // otherwise. If false is returned for any cookie then no cookies will be sent + // with the request. + public bool CanGetCookie(CefSharp.Cookie cookie) { + return true; + } + // + // Summary: + // Return true if the specified cookie returned with the response can be set + // or false otherwise. + public bool CanSetCookie(CefSharp.Cookie cookie) { + return true; + } + } -} +} \ No newline at end of file diff --git a/src/Handlers/SchemeHandlerFactory.cs b/src/Handlers/SchemeHandlerFactory.cs index 5abf266..b398257 100644 --- a/src/Handlers/SchemeHandlerFactory.cs +++ b/src/Handlers/SchemeHandlerFactory.cs @@ -1,15 +1,12 @@ using CefSharp; -namespace SharpBrowser -{ - public class SchemeHandlerFactory : ISchemeHandlerFactory +namespace SharpBrowser { + internal class SchemeHandlerFactory : ISchemeHandlerFactory { - public const string SchemeName = "chrome"; - public const string SchemeNameTest = "test"; public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request) { - return new SchemeHandler(); + return new SchemeHandler(MainForm.Instance); } } } \ No newline at end of file diff --git a/src/MainForm.Designer.cs b/src/MainForm.Designer.cs index c07b334..0460d68 100644 --- a/src/MainForm.Designer.cs +++ b/src/MainForm.Designer.cs @@ -33,21 +33,28 @@ private void InitializeComponent() this.menuStripTab = new System.Windows.Forms.ContextMenuStrip(this.components); this.menuCloseTab = new System.Windows.Forms.ToolStripMenuItem(); this.menuCloseOtherTabs = new System.Windows.Forms.ToolStripMenuItem(); - this.bRefresh = new System.Windows.Forms.Button(); - this.bStop = new System.Windows.Forms.Button(); - this.bForward = new System.Windows.Forms.Button(); - this.bBack = new System.Windows.Forms.Button(); + this.BtnRefresh = new System.Windows.Forms.Button(); + this.BtnStop = new System.Windows.Forms.Button(); + this.BtnForward = new System.Windows.Forms.Button(); + this.BtnBack = new System.Windows.Forms.Button(); this.timer1 = new System.Windows.Forms.Timer(this.components); - this.bDownloads = new System.Windows.Forms.Button(); - this.txtUrl = new System.Windows.Forms.TextBox(); - this.panel1 = new System.Windows.Forms.Panel(); - this.tabPages = new FarsiLibrary.Win.FATabStrip(); + this.BtnDownloads = new System.Windows.Forms.Button(); + this.TxtURL = new System.Windows.Forms.TextBox(); + this.PanelToolbar = new System.Windows.Forms.Panel(); + this.TabPages = new FarsiLibrary.Win.FATabStrip(); this.tabStrip1 = new FarsiLibrary.Win.FATabStripItem(); this.tabStripAdd = new FarsiLibrary.Win.FATabStripItem(); + this.PanelStatus = new System.Windows.Forms.Panel(); + this.PanelSearch = new System.Windows.Forms.Panel(); + this.BtnNextSearch = new System.Windows.Forms.Button(); + this.BtnPrevSearch = new System.Windows.Forms.Button(); + this.BtnCloseSearch = new System.Windows.Forms.Button(); + this.TxtSearch = new System.Windows.Forms.TextBox(); this.menuStripTab.SuspendLayout(); - this.panel1.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.tabPages)).BeginInit(); - this.tabPages.SuspendLayout(); + this.PanelToolbar.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.TabPages)).BeginInit(); + this.TabPages.SuspendLayout(); + this.PanelSearch.SuspendLayout(); this.SuspendLayout(); // // menuStripTab @@ -73,134 +80,137 @@ private void InitializeComponent() this.menuCloseOtherTabs.Text = "Close other tabs"; this.menuCloseOtherTabs.Click += new System.EventHandler(this.menuCloseOtherTabs_Click); // - // bRefresh - // - this.bRefresh.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.bRefresh.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.bRefresh.ForeColor = System.Drawing.Color.White; - this.bRefresh.Image = ((System.Drawing.Image)(resources.GetObject("bRefresh.Image"))); - this.bRefresh.Location = new System.Drawing.Point(854, 2); - this.bRefresh.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.bRefresh.Name = "bRefresh"; - this.bRefresh.Size = new System.Drawing.Size(35, 35); - this.bRefresh.TabIndex = 3; - this.bRefresh.UseVisualStyleBackColor = true; - this.bRefresh.Click += new System.EventHandler(this.bRefresh_Click); - // - // bStop - // - this.bStop.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.bStop.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.bStop.ForeColor = System.Drawing.Color.White; - this.bStop.Image = ((System.Drawing.Image)(resources.GetObject("bStop.Image"))); - this.bStop.Location = new System.Drawing.Point(854, 2); - this.bStop.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.bStop.Name = "bStop"; - this.bStop.Size = new System.Drawing.Size(35, 35); - this.bStop.TabIndex = 2; - this.bStop.UseVisualStyleBackColor = true; - this.bStop.Click += new System.EventHandler(this.bStop_Click); - // - // bForward - // - this.bForward.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.bForward.ForeColor = System.Drawing.Color.White; - this.bForward.Image = ((System.Drawing.Image)(resources.GetObject("bForward.Image"))); - this.bForward.Location = new System.Drawing.Point(39, 2); - this.bForward.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.bForward.Name = "bForward"; - this.bForward.Size = new System.Drawing.Size(35, 35); - this.bForward.TabIndex = 1; - this.bForward.UseVisualStyleBackColor = true; - this.bForward.Click += new System.EventHandler(this.bForward_Click); - // - // bBack - // - this.bBack.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.bBack.ForeColor = System.Drawing.Color.White; - this.bBack.Image = ((System.Drawing.Image)(resources.GetObject("bBack.Image"))); - this.bBack.Location = new System.Drawing.Point(2, 2); - this.bBack.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.bBack.Name = "bBack"; - this.bBack.Size = new System.Drawing.Size(35, 35); - this.bBack.TabIndex = 0; - this.bBack.UseVisualStyleBackColor = true; - this.bBack.Click += new System.EventHandler(this.bBack_Click); + // BtnRefresh + // + this.BtnRefresh.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.BtnRefresh.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnRefresh.ForeColor = System.Drawing.Color.White; + this.BtnRefresh.Image = ((System.Drawing.Image)(resources.GetObject("BtnRefresh.Image"))); + this.BtnRefresh.Location = new System.Drawing.Point(878, 0); + this.BtnRefresh.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnRefresh.Name = "BtnRefresh"; + this.BtnRefresh.Size = new System.Drawing.Size(25, 30); + this.BtnRefresh.TabIndex = 3; + this.BtnRefresh.UseVisualStyleBackColor = true; + this.BtnRefresh.Click += new System.EventHandler(this.bRefresh_Click); + // + // BtnStop + // + this.BtnStop.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.BtnStop.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnStop.ForeColor = System.Drawing.Color.White; + this.BtnStop.Image = ((System.Drawing.Image)(resources.GetObject("BtnStop.Image"))); + this.BtnStop.Location = new System.Drawing.Point(878, -2); + this.BtnStop.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnStop.Name = "BtnStop"; + this.BtnStop.Size = new System.Drawing.Size(25, 30); + this.BtnStop.TabIndex = 2; + this.BtnStop.UseVisualStyleBackColor = true; + this.BtnStop.Click += new System.EventHandler(this.bStop_Click); + // + // BtnForward + // + this.BtnForward.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnForward.ForeColor = System.Drawing.Color.White; + this.BtnForward.Image = ((System.Drawing.Image)(resources.GetObject("BtnForward.Image"))); + this.BtnForward.Location = new System.Drawing.Point(29, 0); + this.BtnForward.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnForward.Name = "BtnForward"; + this.BtnForward.Size = new System.Drawing.Size(25, 30); + this.BtnForward.TabIndex = 1; + this.BtnForward.UseVisualStyleBackColor = true; + this.BtnForward.Click += new System.EventHandler(this.bForward_Click); + // + // BtnBack + // + this.BtnBack.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnBack.ForeColor = System.Drawing.Color.White; + this.BtnBack.Image = ((System.Drawing.Image)(resources.GetObject("BtnBack.Image"))); + this.BtnBack.Location = new System.Drawing.Point(2, 0); + this.BtnBack.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnBack.Name = "BtnBack"; + this.BtnBack.Size = new System.Drawing.Size(25, 30); + this.BtnBack.TabIndex = 0; + this.BtnBack.UseVisualStyleBackColor = true; + this.BtnBack.Click += new System.EventHandler(this.bBack_Click); // // timer1 // this.timer1.Interval = 50; this.timer1.Tick += new System.EventHandler(this.timer1_Tick); // - // bDownloads + // BtnDownloads // - this.bDownloads.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.bDownloads.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.bDownloads.ForeColor = System.Drawing.Color.White; - this.bDownloads.Image = ((System.Drawing.Image)(resources.GetObject("bDownloads.Image"))); - this.bDownloads.Location = new System.Drawing.Point(892, 2); - this.bDownloads.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.bDownloads.Name = "bDownloads"; - this.bDownloads.Size = new System.Drawing.Size(35, 35); - this.bDownloads.TabIndex = 4; - this.bDownloads.UseVisualStyleBackColor = true; - this.bDownloads.Click += new System.EventHandler(this.bDownloads_Click); + this.BtnDownloads.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.BtnDownloads.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnDownloads.ForeColor = System.Drawing.Color.White; + this.BtnDownloads.Image = ((System.Drawing.Image)(resources.GetObject("BtnDownloads.Image"))); + this.BtnDownloads.Location = new System.Drawing.Point(906, 0); + this.BtnDownloads.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnDownloads.Name = "BtnDownloads"; + this.BtnDownloads.Size = new System.Drawing.Size(25, 30); + this.BtnDownloads.TabIndex = 4; + this.BtnDownloads.Tag = "Downloads"; + this.BtnDownloads.UseVisualStyleBackColor = true; + this.BtnDownloads.Click += new System.EventHandler(this.bDownloads_Click); // - // txtUrl + // TxtURL // - this.txtUrl.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.TxtURL.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.txtUrl.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.txtUrl.Font = new System.Drawing.Font("Segoe UI", 13.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.txtUrl.Location = new System.Drawing.Point(85, 5); - this.txtUrl.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.txtUrl.Name = "txtUrl"; - this.txtUrl.Size = new System.Drawing.Size(763, 31); - this.txtUrl.TabIndex = 5; - this.txtUrl.Click += new System.EventHandler(this.txtUrl_Click); - this.txtUrl.TextChanged += new System.EventHandler(this.txtUrl_TextChanged); - this.txtUrl.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txtUrl_KeyDown_1); - // - // panel1 - // - this.panel1.BackColor = System.Drawing.Color.White; - this.panel1.Controls.Add(this.bRefresh); - this.panel1.Controls.Add(this.txtUrl); - this.panel1.Controls.Add(this.bDownloads); - this.panel1.Controls.Add(this.bForward); - this.panel1.Controls.Add(this.bBack); - this.panel1.Controls.Add(this.bStop); - this.panel1.Dock = System.Windows.Forms.DockStyle.Top; - this.panel1.Location = new System.Drawing.Point(0, 0); - this.panel1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(933, 40); - this.panel1.TabIndex = 6; - // - // tabPages - // - this.tabPages.ContextMenuStrip = this.menuStripTab; - this.tabPages.Dock = System.Windows.Forms.DockStyle.Fill; - this.tabPages.Font = new System.Drawing.Font("Segoe UI", 10.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.tabPages.Items.AddRange(new FarsiLibrary.Win.FATabStripItem[] { + this.TxtURL.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.TxtURL.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.TxtURL.Location = new System.Drawing.Point(60, 5); + this.TxtURL.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.TxtURL.Name = "TxtURL"; + this.TxtURL.Size = new System.Drawing.Size(812, 27); + this.TxtURL.TabIndex = 5; + this.TxtURL.Click += new System.EventHandler(this.txtUrl_Click); + this.TxtURL.TextChanged += new System.EventHandler(this.txtUrl_TextChanged); + this.TxtURL.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TxtURL_KeyDown); + // + // PanelToolbar + // + this.PanelToolbar.BackColor = System.Drawing.Color.White; + this.PanelToolbar.Controls.Add(this.TxtURL); + this.PanelToolbar.Controls.Add(this.BtnDownloads); + this.PanelToolbar.Controls.Add(this.BtnForward); + this.PanelToolbar.Controls.Add(this.BtnBack); + this.PanelToolbar.Controls.Add(this.BtnRefresh); + this.PanelToolbar.Controls.Add(this.BtnStop); + this.PanelToolbar.Dock = System.Windows.Forms.DockStyle.Top; + this.PanelToolbar.Location = new System.Drawing.Point(0, 0); + this.PanelToolbar.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.PanelToolbar.Name = "PanelToolbar"; + this.PanelToolbar.Size = new System.Drawing.Size(934, 30); + this.PanelToolbar.TabIndex = 6; + // + // TabPages + // + this.TabPages.ContextMenuStrip = this.menuStripTab; + this.TabPages.Dock = System.Windows.Forms.DockStyle.Fill; + this.TabPages.Font = new System.Drawing.Font("Segoe UI", 10.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.TabPages.Items.AddRange(new FarsiLibrary.Win.FATabStripItem[] { this.tabStrip1, this.tabStripAdd}); - this.tabPages.Location = new System.Drawing.Point(0, 40); - this.tabPages.Name = "tabPages"; - this.tabPages.SelectedItem = this.tabStrip1; - this.tabPages.Size = new System.Drawing.Size(933, 631); - this.tabPages.TabIndex = 4; - this.tabPages.Text = "faTabStrip1"; - this.tabPages.TabStripItemSelectionChanged += new FarsiLibrary.Win.TabStripItemChangedHandler(this.tabPages_TabStripItemSelectionChanged); + this.TabPages.Location = new System.Drawing.Point(0, 30); + this.TabPages.Name = "TabPages"; + this.TabPages.SelectedItem = this.tabStrip1; + this.TabPages.Size = new System.Drawing.Size(934, 621); + this.TabPages.TabIndex = 4; + this.TabPages.Text = "faTabStrip1"; + this.TabPages.TabStripItemSelectionChanged += new FarsiLibrary.Win.TabStripItemChangedHandler(this.OnTabsChanged); + this.TabPages.TabStripItemClosed += new System.EventHandler(this.OnTabClosed); + this.TabPages.MouseClick += new System.Windows.Forms.MouseEventHandler(this.tabPages_MouseClick); // // tabStrip1 // this.tabStrip1.IsDrawn = true; this.tabStrip1.Name = "tabStrip1"; this.tabStrip1.Selected = true; - this.tabStrip1.Size = new System.Drawing.Size(931, 601); + this.tabStrip1.Size = new System.Drawing.Size(932, 591); this.tabStrip1.TabIndex = 0; - this.tabStrip1.Title = "Tab1"; + this.tabStrip1.Title = "Loading..."; // // tabStripAdd // @@ -211,12 +221,96 @@ private void InitializeComponent() this.tabStripAdd.TabIndex = 1; this.tabStripAdd.Title = "+"; // + // PanelStatus + // + this.PanelStatus.Dock = System.Windows.Forms.DockStyle.Bottom; + this.PanelStatus.Location = new System.Drawing.Point(0, 651); + this.PanelStatus.Name = "PanelStatus"; + this.PanelStatus.Size = new System.Drawing.Size(934, 20); + this.PanelStatus.TabIndex = 8; + // + // PanelSearch + // + this.PanelSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.PanelSearch.BackColor = System.Drawing.Color.White; + this.PanelSearch.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.PanelSearch.Controls.Add(this.BtnNextSearch); + this.PanelSearch.Controls.Add(this.BtnPrevSearch); + this.PanelSearch.Controls.Add(this.BtnCloseSearch); + this.PanelSearch.Controls.Add(this.TxtSearch); + this.PanelSearch.Location = new System.Drawing.Point(625, 41); + this.PanelSearch.Name = "PanelSearch"; + this.PanelSearch.Size = new System.Drawing.Size(307, 40); + this.PanelSearch.TabIndex = 9; + this.PanelSearch.Visible = false; + // + // BtnNextSearch + // + this.BtnNextSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.BtnNextSearch.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnNextSearch.ForeColor = System.Drawing.Color.White; + this.BtnNextSearch.Image = ((System.Drawing.Image)(resources.GetObject("BtnNextSearch.Image"))); + this.BtnNextSearch.Location = new System.Drawing.Point(239, 4); + this.BtnNextSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnNextSearch.Name = "BtnNextSearch"; + this.BtnNextSearch.Size = new System.Drawing.Size(25, 30); + this.BtnNextSearch.TabIndex = 9; + this.BtnNextSearch.Tag = "Find next (Enter)"; + this.BtnNextSearch.UseVisualStyleBackColor = true; + this.BtnNextSearch.Click += new System.EventHandler(this.BtnNextSearch_Click); + // + // BtnPrevSearch + // + this.BtnPrevSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.BtnPrevSearch.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnPrevSearch.ForeColor = System.Drawing.Color.White; + this.BtnPrevSearch.Image = ((System.Drawing.Image)(resources.GetObject("BtnPrevSearch.Image"))); + this.BtnPrevSearch.Location = new System.Drawing.Point(206, 4); + this.BtnPrevSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnPrevSearch.Name = "BtnPrevSearch"; + this.BtnPrevSearch.Size = new System.Drawing.Size(25, 30); + this.BtnPrevSearch.TabIndex = 8; + this.BtnPrevSearch.Tag = "Find previous (Shift+Enter)"; + this.BtnPrevSearch.UseVisualStyleBackColor = true; + this.BtnPrevSearch.Click += new System.EventHandler(this.BtnPrevSearch_Click); + // + // BtnCloseSearch + // + this.BtnCloseSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.BtnCloseSearch.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.BtnCloseSearch.ForeColor = System.Drawing.Color.White; + this.BtnCloseSearch.Image = ((System.Drawing.Image)(resources.GetObject("BtnCloseSearch.Image"))); + this.BtnCloseSearch.Location = new System.Drawing.Point(272, 4); + this.BtnCloseSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.BtnCloseSearch.Name = "BtnCloseSearch"; + this.BtnCloseSearch.Size = new System.Drawing.Size(25, 30); + this.BtnCloseSearch.TabIndex = 7; + this.BtnCloseSearch.Tag = "Close (Esc)"; + this.BtnCloseSearch.UseVisualStyleBackColor = true; + this.BtnCloseSearch.Click += new System.EventHandler(this.BtnClearSearch_Click); + // + // TxtSearch + // + this.TxtSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.TxtSearch.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.TxtSearch.Font = new System.Drawing.Font("Segoe UI", 13.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.TxtSearch.Location = new System.Drawing.Point(10, 6); + this.TxtSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.TxtSearch.Name = "TxtSearch"; + this.TxtSearch.Size = new System.Drawing.Size(181, 31); + this.TxtSearch.TabIndex = 6; + this.TxtSearch.TextChanged += new System.EventHandler(this.TxtSearch_TextChanged); + this.TxtSearch.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TxtSearch_KeyDown); + // // MainForm // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; - this.ClientSize = new System.Drawing.Size(933, 671); - this.Controls.Add(this.tabPages); - this.Controls.Add(this.panel1); + this.ClientSize = new System.Drawing.Size(934, 671); + this.Controls.Add(this.PanelSearch); + this.Controls.Add(this.TabPages); + this.Controls.Add(this.PanelToolbar); + this.Controls.Add(this.PanelStatus); this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "MainForm"; @@ -226,30 +320,38 @@ private void InitializeComponent() this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); this.Load += new System.EventHandler(this.MainForm_Load); this.menuStripTab.ResumeLayout(false); - this.panel1.ResumeLayout(false); - this.panel1.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.tabPages)).EndInit(); - this.tabPages.ResumeLayout(false); + this.PanelToolbar.ResumeLayout(false); + this.PanelToolbar.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.TabPages)).EndInit(); + this.TabPages.ResumeLayout(false); + this.PanelSearch.ResumeLayout(false); + this.PanelSearch.PerformLayout(); this.ResumeLayout(false); } #endregion - private FarsiLibrary.Win.FATabStrip tabPages; + private FarsiLibrary.Win.FATabStrip TabPages; private FarsiLibrary.Win.FATabStripItem tabStrip1; private FarsiLibrary.Win.FATabStripItem tabStripAdd; private System.Windows.Forms.Timer timer1; private System.Windows.Forms.ContextMenuStrip menuStripTab; private System.Windows.Forms.ToolStripMenuItem menuCloseTab; private System.Windows.Forms.ToolStripMenuItem menuCloseOtherTabs; - private System.Windows.Forms.Button bForward; - private System.Windows.Forms.Button bBack; - private System.Windows.Forms.Button bStop; - private System.Windows.Forms.Button bRefresh; - private System.Windows.Forms.Button bDownloads; - private System.Windows.Forms.TextBox txtUrl; - private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Button BtnForward; + private System.Windows.Forms.Button BtnBack; + private System.Windows.Forms.Button BtnStop; + private System.Windows.Forms.Button BtnRefresh; + private System.Windows.Forms.Button BtnDownloads; + private System.Windows.Forms.TextBox TxtURL; + private System.Windows.Forms.Panel PanelToolbar; + private System.Windows.Forms.Panel PanelStatus; + private System.Windows.Forms.Panel PanelSearch; + private System.Windows.Forms.TextBox TxtSearch; + private System.Windows.Forms.Button BtnCloseSearch; + private System.Windows.Forms.Button BtnPrevSearch; + private System.Windows.Forms.Button BtnNextSearch; } } diff --git a/src/MainForm.cs b/src/MainForm.cs index 681049e..b4a2a1d 100644 --- a/src/MainForm.cs +++ b/src/MainForm.cs @@ -10,107 +10,359 @@ using CefSharp; using CefSharp.WinForms; using FarsiLibrary.Win; +using Timer = System.Windows.Forms.Timer; +using System.Drawing; +using System.Reflection; namespace SharpBrowser { - public partial class MainForm : Form { - private const int PROGRESS_DISABLED = -1; - private const int PROGRESS_INDETERMINATE = -2; - private volatile bool closing; - private DownloadHandler dHandler; - private MenuHandler mHandler; - private LifeSpanHandler lHandler; - private KeyboardHandler kHandler; - private FATabStripItem newStrip; - private FATabStripItem downloadsStrip; + + /// + /// The main SharpBrowser form, supporting multiple tabs. + /// We used the x86 version of CefSharp V51, so the app works on 32-bit and 64-bit machines. + /// If you would only like to support 64-bit machines, simply change the DLL references. + /// + internal partial class MainForm : Form { + private string appPath = Path.GetDirectoryName(Application.ExecutablePath) + @"\"; - public Dictionary downloads; - public Dictionary downloadNames; - public List downloadCancelRequests; - public HostHandler host; - private static string ChromeAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.79 Safari/537.1"; - private string startURL = "about:blank"; - private string downloadsURL = "chrome://storage/downloads.htm"; + public static MainForm Instance; + + public static string Branding = "SharpBrowser"; + public static string UserAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36"; + public static string HomepageURL = "https://www.google.com"; + public static string NewTabURL = "about:blank"; + public static string DownloadsURL = "sharpbrowser://storage/downloads.html"; + public static string FileNotFoundURL = "sharpbrowser://storage/errors/notFound.html"; + public static string CannotConnectURL = "sharpbrowser://storage/errors/cannotConnect.html"; + public static string SearchURL = "https://www.google.com/#q="; + public bool WebSecurity = true; + public bool CrossDomainSecurity = true; + public bool WebGL = true; + + + + public MainForm() { + + Instance = this; + + InitializeComponent(); + + InitBrowser(); + + SetFormTitle(null); + + } private void MainForm_Load(object sender, EventArgs e) { - this.Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath); + InitAppIcon(); + InitTooltips(this.Controls); + InitHotkeys(); - SetStatusText("Ready"); - SetStatusProgress(PROGRESS_DISABLED); } - private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { + #region App Icon + + /// + /// embedding the resource using the Visual Studio designer results in a blurry icon. + /// the best way to get a non-blurry icon for Windows 7 apps. + /// + private void InitAppIcon() { + assembly = Assembly.GetAssembly(typeof(MainForm)); + Icon = new Icon(GetResourceStream("sharpbrowser.ico"), new Size(64, 64)); + } + + public static Assembly assembly = null; + public Stream GetResourceStream(string filename, bool withNamespace = true) { try { - foreach (TabPage tab in tabPages.Items) { - ChromiumWebBrowser browser = (ChromiumWebBrowser)tab.Controls[0]; - browser.Dispose(); + return assembly.GetManifestResourceStream("SharpBrowser.Resources." + filename); + } catch (System.Exception ex) { } + return null; + } + + #endregion + + #region Tooltips & Hotkeys + + /// + /// these hotkeys work when the user is focussed on the .NET form and its controls, + /// AND when the user is focussed on the browser (CefSharp portion) + /// + private void InitHotkeys() { + + // browser hotkeys + KeyboardHandler.AddHotKey(this, CloseActiveTab, Keys.W, true); + KeyboardHandler.AddHotKey(this, CloseActiveTab, Keys.Escape, true); + KeyboardHandler.AddHotKey(this, AddBlankWindow, Keys.N, true); + KeyboardHandler.AddHotKey(this, AddBlankTab, Keys.T, true); + KeyboardHandler.AddHotKey(this, RefreshActiveTab, Keys.F5); + KeyboardHandler.AddHotKey(this, OpenDeveloperTools, Keys.F12); + KeyboardHandler.AddHotKey(this, NextTab, Keys.Tab, true); + KeyboardHandler.AddHotKey(this, PrevTab, Keys.Tab, true, true); + + // search hotkeys + KeyboardHandler.AddHotKey(this, OpenSearch, Keys.F, true); + KeyboardHandler.AddHotKey(this, CloseSearch, Keys.Escape); + KeyboardHandler.AddHotKey(this, StopActiveTab, Keys.Escape); + + + } + + /// + /// we activate all the tooltips stored in the Tag property of the buttons + /// + public void InitTooltips(System.Windows.Forms.Control.ControlCollection parent) { + foreach (Control ui in parent) { + Button btn = ui as Button; + if (btn != null) { + if (btn.Tag != null) { + ToolTip tip = new ToolTip(); + tip.ReshowDelay = tip.InitialDelay = 200; + tip.ShowAlways = true; + tip.SetToolTip(btn, btn.Tag.ToString()); + } + } + Panel panel = ui as Panel; + if (panel != null) { + InitTooltips(panel.Controls); } - } - catch (System.Exception ex) - { - } } - public MainForm() { - InitializeComponent(); + #endregion + + #region Web Browser & Tabs + + private FATabStripItem newStrip; + private FATabStripItem downloadsStrip; + + private string currentFullURL; + private string currentCleanURL; + private string currentTitle; + + public HostHandler host; + private DownloadHandler dHandler; + private ContextMenuHandler mHandler; + private LifeSpanHandler lHandler; + private KeyboardHandler kHandler; + private RequestHandler rHandler; + + /// + /// this is done just once, to globally initialize CefSharp/CEF + /// + private void InitBrowser() { CefSettings settings = new CefSettings(); settings.RegisterScheme(new CefCustomScheme { - SchemeName = SchemeHandlerFactory.SchemeName, + SchemeName = "sharpbrowser", SchemeHandlerFactory = new SchemeHandlerFactory() }); - settings.RegisterScheme(new CefCustomScheme { - SchemeName = SchemeHandlerFactory.SchemeNameTest, - SchemeHandlerFactory = new SchemeHandlerFactory() - }); + settings.UserAgent = UserAgent; - settings.UserAgent = ChromeAgent; + settings.IgnoreCertificateErrors = true; + + settings.CachePath = GetAppDir("Cache"); Cef.Initialize(settings); dHandler = new DownloadHandler(this); lHandler = new LifeSpanHandler(this); - mHandler = new MenuHandler(this); + mHandler = new ContextMenuHandler(this); kHandler = new KeyboardHandler(this); + rHandler = new RequestHandler(this); - downloads = new Dictionary(); - downloadNames = new Dictionary(); - downloadCancelRequests = new List(); + InitDownloads(); host = new HostHandler(this); - txtUrl.Text = startURL; - AddNewBrowser(tabStrip1, startURL); + AddNewBrowser(tabStrip1, HomepageURL); - SetFormTitle(null); + } + + /// + /// this is done every time a new tab is openede + /// + private void ConfigureBrowser(ChromiumWebBrowser browser) { + + BrowserSettings config = new BrowserSettings(); + + config.FileAccessFromFileUrls = (!CrossDomainSecurity).ToCefState(); + config.UniversalAccessFromFileUrls = (!CrossDomainSecurity).ToCefState(); + config.WebSecurity = WebSecurity.ToCefState(); + config.WebGl = WebGL.ToCefState(); + + browser.BrowserSettings = config; + + } + + + private static string GetAppDir(string name) { + string winXPDir = @"C:\Documents and Settings\All Users\Application Data\"; + if (Directory.Exists(winXPDir)) { + return winXPDir + Branding + @"\" + name + @"\"; + } + return @"C:\ProgramData\" + Branding + @"\" + name + @"\"; + + } + + private void LoadURL(string url) { + Uri outUri; + string newUrl = url; + string urlLower = url.Trim().ToLower(); + + // UI + SetTabTitle(CurBrowser, "Loading..."); + + // load page + if (urlLower == "localhost") { + + newUrl = "http://localhost/"; + + } else if (url.CheckIfFilePath() || url.CheckIfFilePath2()) { + + newUrl = url.PathToURL(); + + } else { + + Uri.TryCreate(url, UriKind.Absolute, out outUri); + + if (!(urlLower.StartsWith("http") || urlLower.StartsWith("sharpbrowser"))) { + if (outUri == null || outUri.Scheme != Uri.UriSchemeFile) newUrl = "http://" + url; + } + + if (urlLower.StartsWith("sharpbrowser:") || + + // load URL if it seems valid + (Uri.TryCreate(newUrl, UriKind.Absolute, out outUri) + && ((outUri.Scheme == Uri.UriSchemeHttp || outUri.Scheme == Uri.UriSchemeHttps) && newUrl.Contains(".") || outUri.Scheme == Uri.UriSchemeFile))) { + + } else { + + // run search if unknown URL + newUrl = SearchURL + HttpUtility.UrlEncode(url); + + } + + } + + // load URL + CurBrowser.Load(newUrl); + + // set URL in UI + SetFormURL(newUrl); + + // always enable back btn + EnableBackButton(true); + EnableForwardButton(false); } - public ChromiumWebBrowser AddNewBrowserTab(String url, bool focusNewTab = true) { + private void SetFormTitle(string tabName) { + + if (tabName.CheckIfValid()) { + + this.Text = tabName + " - " + Branding; + currentTitle = tabName; + + } else { + + this.Text = Branding; + currentTitle = "New Tab"; + } + + } + + private void SetFormURL(string URL) { + + currentFullURL = URL; + currentCleanURL = CleanURL(URL); + + TxtURL.Text = currentCleanURL; + + CurTab.CurURL = currentFullURL; + + CloseSearch(); + + } + + private string CleanURL(string url) { + if (url.BeginsWith("about:")) { + return ""; + } + url = url.RemovePrefix("http://"); + url = url.RemovePrefix("https://"); + url = url.RemovePrefix("file://"); + url = url.RemovePrefix("/"); + return url.DecodeURL(); + } + private bool IsBlank(string url) { + return (url == "" || url == "about:blank"); + } + private bool IsBlankOrSystem(string url) { + return (url == "" || url.BeginsWith("about:") || url.BeginsWith("chrome:") || url.BeginsWith("sharpbrowser:")); + } + + public void AddBlankWindow() { + + // open a new instance of the browser + + ProcessStartInfo info = new ProcessStartInfo(Application.ExecutablePath, ""); + //info.WorkingDirectory = workingDir ?? exePath.GetPathDir(true); + info.LoadUserProfile = true; + + info.UseShellExecute = false; + info.RedirectStandardError = true; + info.RedirectStandardOutput = true; + info.RedirectStandardInput = true; + + Process.Start(info); + } + public void AddBlankTab() { + AddNewBrowserTab(""); + this.InvokeOnParent(delegate() { + TxtURL.Focus(); + }); + } + + public ChromiumWebBrowser AddNewBrowserTab(string url, bool focusNewTab = true, string refererUrl = null) { return (ChromiumWebBrowser)this.Invoke((Func)delegate { + + // check if already exists + foreach (FATabStripItem tab in TabPages.Items) { + SharpTab tab2 = (SharpTab)tab.Tag; + if (tab2 != null && (tab2.CurURL == url)) { + TabPages.SelectedItem = tab; + return tab2.Browser; + } + } + FATabStripItem tabStrip = new FATabStripItem(); - tabStrip.Title = "about:blank"; - tabPages.Items.Insert(tabPages.Items.Count - 1, tabStrip); + tabStrip.Title = "New Tab"; + TabPages.Items.Insert(TabPages.Items.Count - 1, tabStrip); newStrip = tabStrip; - ChromiumWebBrowser browser = AddNewBrowser(newStrip, url); + + SharpTab newTab = AddNewBrowser(newStrip, url); + newTab.RefererURL = refererUrl; if (focusNewTab) timer1.Enabled = true; - return browser; + return newTab.Browser; }); } - - private ChromiumWebBrowser AddNewBrowser(FATabStripItem tabStrip, String url) { - if (url == "") url = startURL; + private SharpTab AddNewBrowser(FATabStripItem tabStrip, String url) { + if (url == "") url = NewTabURL; ChromiumWebBrowser browser = new ChromiumWebBrowser(url); + // set config + ConfigureBrowser(browser); + + // set layout browser.Dock = DockStyle.Fill; tabStrip.Controls.Add(browser); browser.BringToFront(); + // add events browser.StatusMessage += Browser_StatusMessage; browser.LoadingStateChanged += Browser_LoadingStateChanged; browser.TitleChanged += Browser_TitleChanged; @@ -120,27 +372,144 @@ private ChromiumWebBrowser AddNewBrowser(FATabStripItem tabStrip, String url) { browser.MenuHandler = mHandler; browser.LifeSpanHandler = lHandler; browser.KeyboardHandler = kHandler; - - if (url.StartsWith("chrome:")) { + browser.RequestHandler = rHandler; + + // new tab obj + SharpTab tab = new SharpTab { + IsOpen = true, + Browser = browser, + Tab = tabStrip, + OrigURL = url, + CurURL = url, + Title = "New Tab", + DateCreated = DateTime.Now + }; + + // save tab obj in tabstrip + tabStrip.Tag = tab; + + if (url.StartsWith("sharpbrowser:")) { browser.RegisterAsyncJsObject("host", host, true); } - return browser; + return tab; + } + + public SharpTab GetTabByBrowser(IWebBrowser browser) { + foreach (FATabStripItem tab2 in TabPages.Items) { + SharpTab tab = (SharpTab)(tab2.Tag); + if (tab != null && tab.Browser == browser) { + return tab; + } + } + return null; + } + + public void RefreshActiveTab() { + CurBrowser.Load(CurBrowser.Address); + } + + public void CloseActiveTab() { + if (CurTab != null/* && TabPages.Items.Count > 2*/) { + + // remove tab and save its index + int index = TabPages.Items.IndexOf(TabPages.SelectedItem); + TabPages.RemoveTab(TabPages.SelectedItem); + + // keep tab at same index focussed + if ((TabPages.Items.Count - 1) > index) { + TabPages.SelectedItem = TabPages.Items[index]; + } + } + } + + private void OnTabClosed(object sender, EventArgs e) { + + } + + private void OnTabClosing(FarsiLibrary.Win.TabStripItemClosingEventArgs e) { + + // exit if invalid tab + if (CurTab == null){ + e.Cancel = true; + return; + } + + // add a blank tab if the very last tab is closed! + if (TabPages.Items.Count <= 2) { + AddBlankTab(); + //e.Cancel = true; + } + + } + + private void StopActiveTab() { + CurBrowser.Stop(); + } + + private bool IsOnFirstTab() { + return TabPages.SelectedItem == TabPages.Items[0]; + } + private bool IsOnLastTab() { + return TabPages.SelectedItem == TabPages.Items[TabPages.Items.Count - 2]; } - public ChromiumWebBrowser Browser { + private int CurIndex { get { - if (tabPages.SelectedItem.Controls.Count > 0) - return (ChromiumWebBrowser)tabPages.SelectedItem.Controls[0]; - else - return null; + return TabPages.Items.IndexOf(TabPages.SelectedItem); + } + set { + TabPages.SelectedItem = TabPages.Items[value]; + } + } + private int LastIndex { + get { + return TabPages.Items.Count - 2; } } - private void SetFormTitle(string tabName) { - if (tabName == null || tabName.Length == 0) { - this.Text = "SharpBrowser"; + private void NextTab() { + if (IsOnLastTab()) { + CurIndex = 0; } else { - this.Text = tabName + " - SharpBrowser"; + CurIndex++; + } + } + private void PrevTab() { + if (IsOnFirstTab()) { + CurIndex = LastIndex; + } else { + CurIndex--; + } + } + + public ChromiumWebBrowser CurBrowser { + get { + if (TabPages.SelectedItem != null && TabPages.SelectedItem.Tag != null) { + return ((SharpTab)TabPages.SelectedItem.Tag).Browser; + } else { + return null; + } + } + } + + public SharpTab CurTab { + get { + if (TabPages.SelectedItem != null && TabPages.SelectedItem.Tag != null) { + return ((SharpTab)TabPages.SelectedItem.Tag); + } else { + return null; + } + } + } + + public int CurTabLoadingDur { + get { + if (TabPages.SelectedItem != null && TabPages.SelectedItem.Tag != null) { + int loadTime = (int)(DateTime.Now - CurTab.DateCreated).TotalMilliseconds; + return loadTime; + } else { + return 0; + } } } @@ -148,46 +517,58 @@ private void Browser_URLChanged(object sender, AddressChangedEventArgs e) { InvokeIfNeeded(() => { // if current tab - if (sender == Browser) { + if (sender == CurBrowser) { - txtUrl.Text = e.Address; + if (!Utils.IsFocussed(TxtURL)) { + SetFormURL(e.Address); + } - SetCanGoBack(Browser.CanGoBack); - SetCanGoForward(Browser.CanGoForward); + EnableBackButton(CurBrowser.CanGoBack); + EnableForwardButton(CurBrowser.CanGoForward); SetTabTitle((ChromiumWebBrowser)sender, "Loading..."); + BtnRefresh.Visible = false; + BtnStop.Visible = true; + + CurTab.DateCreated = DateTime.Now; + } }); } private void Browser_LoadError(object sender, LoadErrorEventArgs e) { - SetErrorText("Load Error:" + e.ErrorCode + ";" + e.ErrorText); + // ("Load Error:" + e.ErrorCode + ";" + e.ErrorText); } private void Browser_TitleChanged(object sender, TitleChangedEventArgs e) { InvokeIfNeeded(() => { - SetTabTitle((ChromiumWebBrowser)sender, e.Title); + ChromiumWebBrowser browser = (ChromiumWebBrowser)sender; + + SetTabTitle(browser, e.Title); }); } - private void SetTabTitle(ChromiumWebBrowser sender, string text) { + private void SetTabTitle(ChromiumWebBrowser browser, string text) { text = text.Trim(); - if (text == "" || text == "about:blank") { + if (IsBlank(text)) { text = "New Tab"; } + // save text + browser.Tag = text; + // get tab of given browser - FATabStripItem tabStrip = (FATabStripItem)sender.Parent; + FATabStripItem tabStrip = (FATabStripItem)browser.Parent; tabStrip.Title = text; // if current tab - if (sender == Browser) { + if (browser == CurBrowser) { SetFormTitle(text); @@ -195,32 +576,24 @@ private void SetTabTitle(ChromiumWebBrowser sender, string text) { } private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e) { - if (sender == Browser) { + if (sender == CurBrowser) { - SetCanGoBack(e.CanGoBack); - SetCanGoForward(e.CanGoForward); + EnableBackButton(e.CanGoBack); + EnableForwardButton(e.CanGoForward); if (e.IsLoading) { // set title //SetTabTitle(); - // during loading - SetStatusProgress(PROGRESS_INDETERMINATE); - InvokeIfNeeded(() => { - bRefresh.Visible = false; - bStop.Visible = true; - }); } else { // after loaded / stopped - SetStatusProgress(PROGRESS_DISABLED); InvokeIfNeeded(() => { - bRefresh.Visible = true; - bStop.Visible = false; + BtnRefresh.Visible = true; + BtnStop.Visible = false; }); } - SetErrorText(""); } } @@ -232,41 +605,7 @@ public void InvokeIfNeeded(Action action) { } } - private void SetStatusText(string txt) { - //InvokeIfNeeded(() => lblStatus.Text = txt); - } - - private void SetErrorText(string txt) { - // InvokeIfNeeded(() => lblError.Text = txt); - } - - private void SetStatusProgress(int value) { - /*if (closing) - return; - - Invoke((Action)delegate - { - if (value == PROGRESS_DISABLED) - { - statusProgress.Visible = false; - btnLoading.Image = global::SharpBrowser.Properties.Resources.loader2; - } - else if (value == PROGRESS_INDETERMINATE) - { - statusProgress.Visible = true; - statusProgress.Style = ProgressBarStyle.Marquee; - btnLoading.Image = global::SharpBrowser.Properties.Resources.loader; - } - else - { - statusProgress.Visible = true; - statusProgress.Value = Math.Min(100, Math.Max(0, value)); - } - });*/ - } - private void Browser_StatusMessage(object sender, StatusMessageEventArgs e) { - SetStatusText(e.Value); } public void WaitForBrowserToInitialize(ChromiumWebBrowser browser) { @@ -275,139 +614,76 @@ public void WaitForBrowserToInitialize(ChromiumWebBrowser browser) { } } - private void LoadUrl(string url) { - Uri outUri; - string newUrl = url; - string urlToLower = url.Trim().ToLower(); - - // UI - SetTabTitle(Browser, "Loading..."); - - // load page - if (urlToLower == "localhost") { - Browser.Load("http://localhost/"); - } else { + private void EnableBackButton(bool canGoBack) { + InvokeIfNeeded(() => BtnBack.Enabled = canGoBack); + } + private void EnableForwardButton(bool canGoForward) { + InvokeIfNeeded(() => BtnForward.Enabled = canGoForward); + } - Uri.TryCreate(url, UriKind.Absolute, out outUri); + private void OnTabsChanged(TabStripItemChangedEventArgs e) { - if (!(urlToLower.StartsWith("http") || urlToLower.StartsWith(SchemeHandlerFactory.SchemeName) || urlToLower.StartsWith(SchemeHandlerFactory.SchemeNameTest))) { - if (outUri == null || outUri.Scheme != Uri.UriSchemeFile) newUrl = "http://" + url; - } - if (urlToLower.StartsWith(SchemeHandlerFactory.SchemeName + ":") || urlToLower.StartsWith(SchemeHandlerFactory.SchemeNameTest + ":") || + ChromiumWebBrowser browser = null; + try { + browser = ((ChromiumWebBrowser)e.Item.Controls[0]); + } catch (System.Exception ex) { } - // load URL if it seems valid - (Uri.TryCreate(newUrl, UriKind.Absolute, out outUri) - && ((outUri.Scheme == Uri.UriSchemeHttp || outUri.Scheme == Uri.UriSchemeHttps) && newUrl.Contains(".") || outUri.Scheme == Uri.UriSchemeFile))) { - Browser.Load(newUrl); + if (e.ChangeType == FATabStripItemChangeTypes.SelectionChanged) { + if (TabPages.SelectedItem == tabStripAdd) { + AddBlankTab(); } else { - // search google if unknown URL - string searchURL = "https://www.google.com/?q="; - Browser.Load(searchURL + HttpUtility.UrlEncode(url)); - } + browser = CurBrowser; - } - } - - private void SetCanGoBack(bool canGoBack) { - InvokeIfNeeded(() => bBack.Enabled = canGoBack); - } + SetFormURL(browser.Address); + SetFormTitle(browser.Tag.ConvertToString() ?? "New Tab"); - private void SetCanGoForward(bool canGoForward) { - InvokeIfNeeded(() => bForward.Enabled = canGoForward); - } - private void txtUrl_Enter(object sender, EventArgs e) { - BeginInvoke((Action)delegate { - txtUrl.SelectAll(); - }); - } + EnableBackButton(browser.CanGoBack); + EnableForwardButton(browser.CanGoForward); - private void tabPages_TabStripItemSelectionChanged(TabStripItemChangedEventArgs e) { - if (e.ChangeType == FATabStripItemChangeTypes.SelectionChanged) { - if (tabPages.SelectedItem == tabStripAdd) { - AddNewBrowserTab(""); - } else { - txtUrl.Text = Browser.Address; - if (Browser.IsLoading) SetStatusProgress(PROGRESS_INDETERMINATE); - else SetStatusProgress(PROGRESS_DISABLED); - SetCanGoBack(Browser.CanGoBack); - SetCanGoForward(Browser.CanGoForward); } } + if (e.ChangeType == FATabStripItemChangeTypes.Removed) { if (e.Item == downloadsStrip) downloadsStrip = null; - if (e.Item.Controls.Count > 0) { - ((ChromiumWebBrowser)e.Item.Controls[0]).Dispose(); + if (browser != null) { + browser.Dispose(); } } + if (e.ChangeType == FATabStripItemChangeTypes.Changed) { - if (e.Item.Controls.Count > 0) { - ((ChromiumWebBrowser)e.Item.Controls[0]).Focus(); + if (browser != null) { + if (currentFullURL != "about:blank") { + browser.Focus(); + } } } + } private void timer1_Tick(object sender, EventArgs e) { - tabPages.SelectedItem = newStrip; + TabPages.SelectedItem = newStrip; timer1.Enabled = false; } - public void CloseActiveTab() { - FATabStripItem activeStrip = tabPages.SelectedItem; - if (activeStrip.Controls.Count > 0) { - if (activeStrip.Controls[0] is ChromiumWebBrowser) { - InvokeIfNeeded(() => { - tabPages.RemoveTab(activeStrip); - }); - } - } - } - - public void UpdateDownloadItem(DownloadItem item) { - lock (downloads) { - //SuggestedFileName comes full only in the first attempt so keep it somewhere - if (item.SuggestedFileName != "") downloadNames[item.Id] = item.SuggestedFileName; - - //Set it back if it is empty - if (item.SuggestedFileName == "" && downloadNames.ContainsKey(item.Id)) item.SuggestedFileName = downloadNames[item.Id]; - - downloads[item.Id] = item; - } - } - - private void btnDownloads_Click(object sender, EventArgs e) { - if (downloadsStrip != null && ((ChromiumWebBrowser)downloadsStrip.Controls[0]).Address == downloadsURL) { - tabPages.SelectedItem = downloadsStrip; - } else { - ChromiumWebBrowser brw = AddNewBrowserTab(downloadsURL); - downloadsStrip = (FATabStripItem)brw.Parent; - } - } - private void menuCloseTab_Click(object sender, EventArgs e) { CloseActiveTab(); } private void menuCloseOtherTabs_Click(object sender, EventArgs e) { List listToClose = new List(); - foreach (FATabStripItem tab in tabPages.Items) { - if (tab != tabStripAdd && tab != tabPages.SelectedItem) listToClose.Add(tab); + foreach (FATabStripItem tab in TabPages.Items) { + if (tab != tabStripAdd && tab != TabPages.SelectedItem) listToClose.Add(tab); } foreach (FATabStripItem tab in listToClose) { - tabPages.RemoveTab(tab); + TabPages.RemoveTab(tab); } } - public Dictionary Downloads { - get { - return downloads; - } - } - public List CancelRequests { get { return downloadCancelRequests; @@ -415,11 +691,11 @@ public List CancelRequests { } private void bBack_Click(object sender, EventArgs e) { - Browser.Back(); + CurBrowser.Back(); } private void bForward_Click(object sender, EventArgs e) { - Browser.Forward(); + CurBrowser.Forward(); } private void txtUrl_TextChanged(object sender, EventArgs e) { @@ -427,30 +703,274 @@ private void txtUrl_TextChanged(object sender, EventArgs e) { } private void bDownloads_Click(object sender, EventArgs e) { - AddNewBrowserTab(downloadsURL); + AddNewBrowserTab(DownloadsURL); } private void bRefresh_Click(object sender, EventArgs e) { - Browser.Load(Browser.Address); + RefreshActiveTab(); } private void bStop_Click(object sender, EventArgs e) { - Browser.Stop(); + StopActiveTab(); } + private void TxtURL_KeyDown(object sender, KeyEventArgs e) { + + // if ENTER or CTRL+ENTER pressed + if (e.IsHotkey(Keys.Enter) || e.IsHotkey(Keys.Enter, true)) { + LoadURL(TxtURL.Text); + + // im handling this + e.Handled = true; + e.SuppressKeyPress = true; + + // defocus from url textbox + this.Focus(); + } + + // if full URL copied + if (e.IsHotkey(Keys.C, true) && Utils.IsFullySelected(TxtURL)) { + + // copy the real URL, not the pretty one + Clipboard.SetText(CurBrowser.Address, TextDataFormat.UnicodeText); - private void txtUrl_KeyDown_1(object sender, KeyEventArgs e) { - if (e.KeyCode == Keys.Enter) { - LoadUrl(txtUrl.Text); + // im handling this e.Handled = true; e.SuppressKeyPress = true; } } private void txtUrl_Click(object sender, EventArgs e) { - txtUrl.SelectAll(); + if (!Utils.HasSelection(TxtURL)) { + TxtURL.SelectAll(); + } + } + + private void OpenDeveloperTools() { + CurBrowser.ShowDevTools(); + } + + private void tabPages_MouseClick(object sender, MouseEventArgs e) { + /*if (e.Button == System.Windows.Forms.MouseButtons.Right) { + tabPages.GetTabItemByPoint(this.mouse + }*/ + } + + #endregion + + #region Download Queue + + private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { + + // ask user if they are sure + if (DownloadsInProgress()) { + if (MessageBox.Show("Downloads are in progress. Cancel those and exit?", "Confirm exit", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) { + e.Cancel = true; + return; + } + } + + // dispose all browsers + try { + foreach (TabPage tab in TabPages.Items) { + ChromiumWebBrowser browser = (ChromiumWebBrowser)tab.Controls[0]; + browser.Dispose(); + } + } catch (System.Exception ex) { } + + } + + public Dictionary downloads; + public Dictionary downloadNames; + public List downloadCancelRequests; + + /// + /// we must store download metadata in a list, since CefSharp does not + /// + private void InitDownloads() { + + downloads = new Dictionary(); + downloadNames = new Dictionary(); + downloadCancelRequests = new List(); + + } + + public Dictionary Downloads { + get { + return downloads; + } + } + + public void UpdateDownloadItem(DownloadItem item) { + lock (downloads) { + + // SuggestedFileName comes full only in the first attempt so keep it somewhere + if (item.SuggestedFileName != "") { + downloadNames[item.Id] = item.SuggestedFileName; + } + + // Set it back if it is empty + if (item.SuggestedFileName == "" && downloadNames.ContainsKey(item.Id)) { + item.SuggestedFileName = downloadNames[item.Id]; + } + + downloads[item.Id] = item; + + //UpdateSnipProgress(); + } + } + + public string CalcDownloadPath(DownloadItem item) { + + string itemName = item.SuggestedFileName != null ? item.SuggestedFileName.GetAfterLast(".") + " file" : "downloads"; + + string path = null; + if (path != null) { + return path; + } + + return null; + } + + public bool DownloadsInProgress() { + foreach (DownloadItem item in downloads.Values) { + if (item.IsInProgress) { + return true; + } + } + return false; } - // Browser.ShowDevTools(); + /// + /// open a new tab with the downloads URL + /// + private void btnDownloads_Click(object sender, EventArgs e) { + OpenDownloadsTab(); + } + + public void OpenDownloadsTab() { + if (downloadsStrip != null && ((ChromiumWebBrowser)downloadsStrip.Controls[0]).Address == DownloadsURL) { + TabPages.SelectedItem = downloadsStrip; + } else { + ChromiumWebBrowser brw = AddNewBrowserTab(DownloadsURL); + downloadsStrip = (FATabStripItem)brw.Parent; + } + } + + #endregion + + #region Search Bar + + bool searchOpen = false; + string lastSearch = ""; + + private void OpenSearch() { + if (!searchOpen) { + searchOpen = true; + InvokeIfNeeded(delegate() { + PanelSearch.Visible = true; + TxtSearch.Text = lastSearch; + TxtSearch.Focus(); + TxtSearch.SelectAll(); + }); + } else { + InvokeIfNeeded(delegate() { + TxtSearch.Focus(); + TxtSearch.SelectAll(); + }); + } + } + private void CloseSearch() { + if (searchOpen) { + searchOpen = false; + InvokeIfNeeded(delegate() { + PanelSearch.Visible = false; + CurBrowser.GetBrowser().StopFinding(true); + }); + } + } + + private void BtnClearSearch_Click(object sender, EventArgs e) { + CloseSearch(); + } + + private void BtnPrevSearch_Click(object sender, EventArgs e) { + FindTextOnPage(false); + } + private void BtnNextSearch_Click(object sender, EventArgs e) { + FindTextOnPage(true); + } + + private void FindTextOnPage(bool next = true) { + bool first = lastSearch != TxtSearch.Text; + lastSearch = TxtSearch.Text; + if (lastSearch.CheckIfValid()) { + CurBrowser.GetBrowser().Find(0, lastSearch, true, false, !first); + } else { + CurBrowser.GetBrowser().StopFinding(true); + } + TxtSearch.Focus(); + } + + private void TxtSearch_TextChanged(object sender, EventArgs e) { + FindTextOnPage(true); + } + + private void TxtSearch_KeyDown(object sender, KeyEventArgs e) { + if (e.IsHotkey(Keys.Enter)) { + FindTextOnPage(true); + } + if (e.IsHotkey(Keys.Enter, true) || e.IsHotkey(Keys.Enter, false, true)) { + FindTextOnPage(false); + } + } + + #endregion + + } +} + +/// +/// POCO created for holding data per tab +/// +internal class SharpTab { + + public bool IsOpen; + + public string OrigURL; + public string CurURL; + public string Title; + + public string RefererURL; + + public DateTime DateCreated; + + public FATabStripItem Tab; + public ChromiumWebBrowser Browser; + +} + +/// +/// POCO for holding hotkey data +/// +internal class SharpHotKey { + + public Keys Key; + public int KeyCode; + public bool Ctrl; + public bool Shift; + public bool Alt; + + public Action Callback; + + public SharpHotKey(Action callback, Keys key, bool ctrl = false, bool shift = false, bool alt = false) { + Callback = callback; + Key = key; + KeyCode = (int)key; + Ctrl = ctrl; + Shift = shift; + Alt = alt; + } + } \ No newline at end of file diff --git a/src/MainForm.resx b/src/MainForm.resx index e237cb0..9487f86 100644 --- a/src/MainForm.resx +++ b/src/MainForm.resx @@ -121,63 +121,85 @@ 325, 17 - + - iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAFcSURBVEhL7VXbkcIwDHQHVwIlXAn3wxcDcQlXAiVcCZRACRSAkyuBEighJXBaKZ74JdvD8Hdo - xkMiYq3kldbG/A/bXj/M4I7Gul9jx0e82Hfm//HdUza4bwpyz4OnYMs7wLzJ3gcnoNph+uoOHlXoLlyd - 9w3jTxlDAOYEBBXR0dCmcFl3qyZTBNmNmxyAAsGv2WGyhaSEvyKI5XJDgs9dpOb7FBCpIgS4dwFw92nN - kHIyjKfoY3RIy1oNkh0XHDwPy+rpfQYJ9qTP4Ottz58AS4FyvlV5CCD302cQ45Ino7Wh+Oeu7COtoyHO - TAeZDTJsmRfFVbdOvSB9AHxMyUAWpUithKSmNjNSQSyoGOyitThh9aWAGEAsaZSCCpNPTWoFQVbrnVAH - D7WOnmsAKE1KXjkQ4UvvlSRowAMqbUoRyEvJ4jsewlm64z0AcYbje4khAc+H/21m/hLkviB/Q8P2K7TP - Y3MAAAAASUVORK5CYII= + iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAEZSURBVDhPxVRbEQIxDKwDJCABCfzwxXBXCUhAAlJOAgJoDwlIQMJJgGw7LUlf9AfITKcznXST + TbJR6mc2zFppOyltbtkZ5m1/Hod5QwB3Anu2DwWCL8wFJv/MdtdVHxgPZi4xsAAsgY32rECPH7xpuxSz + F4DekdOcqnVywe0jA40fvAOPWgfDp9Gc2hmO5vh2oIa0rAYmmoIagXKoWQuQ+4Y/4e6fpb977u06UuY0 + ehSBmro/1Idomg2nHJ3lQ4NYM0ktDJD0mkltidIqoaZCEGzcAkgAQaNlglU6aiVAH2ByskM2MNx+DqVK + wpIoUIZayjqtbR/RjIDo0/c1Q8frGTOtE03szKIBBIcbnP3CSJfAw72HMnx75l9XQGc65ZPKTwAAAABJ + RU5ErkJggg== - + - iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAFgSURBVEhL5VXbDcIwDPQGjMAIjMAIjMAIHYER+OUldQRG6AgdgRE6QvFVTuU6TtJU/IGUD9TE - 5zvbZ6K/+d2JznyuD6KOz8BnNKfl77tNgryI9hLYBo3+M8iH7x6rgPhBk8g6CwjGq1hx8NbIAYka+xj/ - ISV/6839PgskDHS2CJCVwXkzMsg7KZ1oG0DyGXEUYeJKiJpGQMhY03YvqVcOAJJCAwTQNgJ5El2yF8oA - oUYTCAA9JnPBGfCU0jTBYJ4TrUbUAHomtgLgnZmtZdPoDDyQEoPwZjPIWgBhoudmycR0xvyxBgA1yHYo - BihcgD0gqxoAYQE7Ci08RLKbgJOV1NiFsNAuHc+JeJEepoW9lIzPet6N6OB2qedDMMBagKx3ia7WhcGo - 84xykwsHerIJi8vK1GxKpsR6IaFkqQtZXFhVm1ExQt9DPruYZhOE/ski16LKzsc6mM7PAtcmkrr/Badb - jKkWSEnEAAAAAElFTkSuQmCC + iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAEtSURBVDhPvZTbDcIwDEW9ASMwAiMwAiMwAiMwAr+8pI7ACB2BETpCRii+lZ06r8ZfVIpUqHN8 + r+OY6F/Pg2j3Ijrxuj6Jxmwd3ToA4nVjwLy1OOZzJzpsggFjyLcHM99DEyrKJg3m35PYTewBAHUWyu9l + CaRGanNoWQGQY4N1geRJvAQtMGT3wgBSMA4w7uMPZ2P1XANWlA1Sc1W7upJaLQrfRPscWINpDO8ZxNlq + W4tc1IJ3bcEAtWKiEHMgo1XXgyHW9mzcq1msQg8MABWTuOM/L3ooqKEXJodStpptG8lo+6zZk7Z+SduI + 9NqVa8KyVksbG8CKzVBk5TiUJLt6cy1uOSC5p7H7pa5QbseXnUIBSj0TB4De+Jq648tmwgSRHksGAW5G + V5V7+joCf9RfGvLkc7XEAAAAAElFTkSuQmCC - + - iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAFFSURBVEhLzVXREcIgDGUDR3AER/DHL8+WERzBERzBERzBAYQ6giMwQkeoBI4rlJDQqqe5y0+v - yUtewosQf2NNJ0Wrz7mro9jdV+/V2aqTkHrgXd3mA0F1Uj345EkBRhy6TR2YB3jOBAjd9qK1FLImbetl - iq4uHhKV/+npjshgS19sNJDBm3E0aVOucALCdYTSRlcWOPd0VXWEbRw9i3iN64GyiqTuI6qMG27TbVHH - HiDGBMQnFm8LvO4lNgX6CohfnpFaBCSiy25SiSr4vtfrrFHsEecgtTJi1WA6k5JKIKtYIYYzAECaMgMK - aMU1WQcgiOlWjvMABUet1RdWt0IgBQDA2NxcLCctQSpogMEtDWlcAn9n4oebHrUqqYcKPBAhlui1rLwl - ufjBxnFgNrmd5QduvT1ScMyAqshBft5OvkS/fhXzAvjjYiB6ootaAAAAAElFTkSuQmCC + iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAEWSURBVDhPvZRNEcIwEIXjAAlIQAIXTgxNJCABCUhAAhIQQFIkIAEJlVB22ybk522bXtiZXDrJ + 1/dedqPUX+rotsrYhzKuo9WXy76Utpc6LU1rZFAOp58enhsZrO0ZK2Kl9i2qhcSm3YuwU7sbzhh3h3u0 + u5ZMQ7mgvPLNGNql1lkBDJ8zI6t5TgiaXBKrEIHDRXTK2/beCig5DBXb1e5W1w4gUwjky1lTxn2CO6yQ + Wqe2xux/zR/Osc35DHuajPRHOYwvL9Q4HWDEpm+LMNpXZB9nEcPzPiyUTT/l+U9KnBSaV18SDE7K3Gjx + yzP2Knh9QOMnSmsuKESyBPPk0T5+CAYYgarfw1guB80H2a5faxu/tqelfV9C2Z1KUaQMeQAAAABJRU5E + rkJggg== - + - iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAFJSURBVEhLzVVdEcIwDI4DJCABCbzwxLFWAhKQgAQkTAICaIcEJFTCJIyGbdzaJk3HeFju9rJr - 8uXnyxeA1VjVaFD2mn7mDIfHZlmeylxA207+zH0+EGanzVMOHiTg4NTsysB6gNdMgLHaFpRvoWjal863 - qP74YyD+TZuvKO/cA4yGROCBHF3Mp03WMY4hQF9NnhRk29gq/Hxiy1c8zIdiHDkLDxDvATJI27aIGEl2 - iSMBgE4IUjV78osTxXeBhUN0P20yBp3GEUDSQYvc9w8KQMI+c0vFtQr/x7ROKyFkhAIqYtYgNwQtad6T - QPYms4ugPhztlnWkgLSts0C4rKSpTIYxEN6YnH5h0qTlpaX7Kqy0kMnAYzQpQH9n+I0vkvpxq3mx5C5l - 4S1JhRAZxynz5FD5Wf7h1vsjhccMWzX5cPkWBy+RkrW8eQMmF2FOGNrFEgAAAABJRU5ErkJggg== + iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAEHSURBVDhPvVTbDcIwEMsGjMAIjMAPX4gmIzACIzACI3QEBiApI3QERmCEckfaKJf6+vjpSZEq + wTm2a9eYTeYc9sb5p3HhS6cbH/821t+WcakapwOV4HTp6bXTga2/YkbhQ4xblS1ErJqjstAmFo6kIgts + uI8x4Z+JVS6Jn6Gn5LWQfmkOwHgJxhRcqBXATr4kpixuRswUuWmPfk8j5BZgU9kolUFAzuCayZVBwMWB + 7bchoA0P4SHnMZ8oTWnNEHayKk1sh6zYWlAmJcZxGxaBgm7T3sh7rSk5U62asClTwWU5MavAx7mYlS8I + V62XPQc2mBrl6zXjL8/aeP2x2WheZLnD4cu2nB+yNJ4saRMhYwAAAABJRU5ErkJggg== 238, 17 - + + + iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAADWSURBVDhPY2AY8sDIyOi+sbHxfxAGsSn2EMwwGD1qIOEQAIYVPxCvB+L9UAyOECQME19vaGio + T9BEkIHA2HyPHhnY+EQZCLIRpJAIQ+MJug5ZAQFDSTMMZjC6oSBXE+VNoCJ5YBjlAzXUI2OQZpihMMOg + fBR1IL0gM+A+BCqejy3QodmOH6QYaikowuBZEVkPyAy4gTiSBzz/GhgY+IMwLsOgBu8nykAcyQeUFkFp + FVRgzIdaRL6BQAP6QWENtSweSJ8H+ZJgGBKTuJGKNkQYkpRA6a0YAGJH+ATjKEoQAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAACfSURBVEhLY2AYBaMhMExDwHefP4PfnnyifAdSB1JPEvDbE8/gv/c/FM/Hq9d/73y4Wp99+sTZ + 47VXHskC/BYhWwBx1HsGl938xFnkv+c8QYswLQBZgt/XKLaDXIPPIootgNmG26L7BH1JXHhBVeG2CBZX + JAYRLtvxWgSMO6IjmpD3sFpETQuwxhEtLECxCJhMqRZEhIJwVH40BAZFCAAAcuCqYT80zm4AAAAASUVO + RK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAACXSURBVEhLY2AYBaMhMHhCwG9PPG0d4793PoP/3v9APJ82FiEsAFlCA4swLaCyRaA4gLgcF6Yw + 6AhbQKGPsFqw5zyD1155Bn8gjekzEn2EywKX3fzgVAWiKbII7FL08Ae6HGYBLO3issh3nz1xydtvTz7C + IiwW4LaI1CDb2w8MkvUYPkB3JsRH6xn8gOpHwWgIjIYA2SEAAOtGqKV2Jt9uAAAAAElFTkSuQmCC + + + iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAADZSURBVEhL7ZXhEcIgDIW7AaN4wACO5AiO4iiOwAgdwREwr0d6OQuWRPzh1d7lmtKGLyTv0mk6 - 5OW9P7N9pQAhhDnGmNnwPBwkAez/Ic0K/G65IFXK/i5sVZY4lXx/UQuBNkq1Er1ZSxaIU4CQkFNDEIDA - DpAdwFntgD4H7IBsAFLUSQ5BOQxfTrQCat9jn2p/ilRrMs00DG8F4si/Ch/r1RjstwF1yDVxhrh3CCJb - IK2Mm+vDIChlkTvKuvxj+N8zEjKXPi0ngk/2gL+BlOaqS6JqvGkkHC7oCYprWGWB1Yw6AAAAAElFTkSu - QmCC + YQUAAADTSURBVEhL7ZTBDYMwDEW9QUfoCIzQS09VCyN0lI7CICWs0FEyAs0nJBTsNDJCnGIpQjgJz/62 + ISpWFDhegev7RE33ocZYuveVGAD82Me5TeYBw7Q4KALimVbHuZnzD4CDOABnLCF7ldXdUwR5v5MoZuAB + KUmzUBkUMpufmwEhghwI+7tYbV6CdAPBv4vJRU53nRr6HzCD1F0VIkkDWi6dmyk1aJx21qauBlORxWbQ + gjjE0qO/LOTG+yIQLQRfG6de8e9SyxVCxsXcoCEYrGJFgbUCX35H/c2nacnTAAAAAElFTkSuQmCC \ No newline at end of file diff --git a/src/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs index 0892489..d4f2120 100644 --- a/src/Properties/AssemblyInfo.cs +++ b/src/Properties/AssemblyInfo.cs @@ -20,7 +20,7 @@ [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("703472b8-b275-4f3f-95b6-426036f8462e")] +[assembly: Guid("ef19e884-45b7-4cde-8b5a-4d2bcede2e0c")] // Version information for an assembly consists of the following four values: // @@ -32,5 +32,5 @@ // 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")] +[assembly: AssemblyVersion("2.0.0.0")] +[assembly: AssemblyFileVersion("2.0.0.0")] diff --git a/src/Resources/blankpage.png b/src/Resources/blankpage.png new file mode 100644 index 0000000..2849b9f Binary files /dev/null and b/src/Resources/blankpage.png differ diff --git a/src/SharpBrowser.csproj b/src/SharpBrowser.csproj index 96d6577..96f36f6 100644 --- a/src/SharpBrowser.csproj +++ b/src/SharpBrowser.csproj @@ -1,7 +1,5 @@  - - Debug @@ -11,14 +9,15 @@ Properties SharpBrowser SharpBrowser - v4.0 + v4.5.2 512 - + + x86 true - full + pdbonly false bin\ DEBUG;TRACE @@ -62,13 +61,9 @@ + - - - True - True - Settings.settings - + @@ -80,9 +75,11 @@ MainForm.cs - + + + MainForm.cs @@ -129,8 +126,6 @@ - - + + + + + + +
+ + +
+

Cannot connect to server

+
+ + +
+ + +
+

SharpBrowser can't connect to the remote server. You may be disconnected from the internet, or the website may be offline.

+
+ + +
+
    +
  • Ensure that your internet connection is working.
  • +
  • Check to see if the URL is correct
  • +
+
+ + + + +
+ + + + +
+ + + \ No newline at end of file diff --git a/src/bin/storage/errors/common.css b/src/bin/storage/errors/common.css new file mode 100644 index 0000000..990e261 --- /dev/null +++ b/src/bin/storage/errors/common.css @@ -0,0 +1,804 @@ + +@namespace html "http://www.w3.org/1999/xhtml"; +@namespace xul "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + +html|body, +xul|page, +xul|window { + font: message-box; + -webkit-appearance: none; + background-color: #fbfbfb; + color: #424e5a; +} + +html|body { + font-size: 18px; + font-weight: normal; + margin: 0; +} + +html|h1 { + font-size: 2.5em; + font-weight: lighter; + line-height: 1.2; + color: #333; + margin: 0; + margin-bottom: .5em; +} + +html|hr { + border-style: solid none none none; + border-color: #c1c1c1; +} + +xul|caption { + -webkit-appearance: none; + margin: 0; +} + +xul|caption > xul|checkbox, +xul|caption > xul|label { + font-size: 1.3rem; + font-weight: bold; + line-height: 22px; + margin: 0 !important; +} + +*|*.main-content { + padding-top: 40px; + -webkit-padding-end: 44px; /* compensate the 4px margin of child elements */ + padding-bottom: 48px; + -webkit-padding-start: 48px; + overflow: auto; +} + +xul|prefpane > xul|*.content-box { + overflow: visible; +} + +/* groupboxes */ + +xul|groupbox { + -webkit-appearance: none; + border: none; + margin: 15px 0 0; + -webkit-padding-start: 0; + -webkit-padding-end: 0; + font-size: 1.25rem; +} + +xul|groupbox xul|label, +xul|groupbox xul|description { + /* !important needed to override toolkit !important rule */ + -webkit-margin-start: 0 !important; + -webkit-margin-end: 0 !important; +} + +/* tabpanels and tabs */ + +xul|tabpanels { + -webkit-appearance: none; + font-size: 1.25rem; + line-height: 22px; + border: none; + padding: 0; + background-color: transparent; + color: inherit; +} + +xul|tabs { + margin-bottom: 15px; + border-top: 1px solid #c1c1c1; + border-bottom: 1px solid #c1c1c1; + background-color: #fbfbfb; +} + +xul|*.tabs-left, +xul|*.tabs-right { + border-bottom: none; +} + +xul|tab { + -webkit-appearance: none; + margin-top: 0; + padding: 4px 20px; + min-height: 44px; + color: #424f5a; + background-color: #fbfbfb; + border-width: 0; + transition: background-color 50ms ease 0s; +} + +xul|tab:hover { + background-color: #ebebeb; +} + +xul|tab[selected] { + background-color: #ebebeb; + padding-bottom: 0; /* compensate the 4px border */ + border-bottom: 4px solid #ff9500; +} + +xul|*.tab-text { + font-size: 1.3rem; + line-height: 22px; +} + +/* html buttons */ + +html|button { + padding: 3px; + /* override forms.css */ + font: inherit; +} + +/* xul buttons and menulists */ + +*|button, +xul|colorpicker[type="button"], +xul|menulist { + -webkit-appearance: none; + height: 30px; + color: #333; + line-height: 20px; + border: 1px solid #c1c1c1; + -webkit-border-top-colors: none !important; + -webkit-border-right-colors: none !important; + -webkit-border-bottom-colors: none !important; + -webkit-border-left-colors: none !important; + border-radius: 2px; + background-color: #fbfbfb; +} + +html|button:enabled:hover, +xul|button:not([disabled="true"]):hover, +xul|colorpicker[type="button"]:not([disabled="true"]):hover, +xul|menulist:not([disabled="true"]):hover { + background-color: #ebebeb; +} + +html|button:enabled:hover:active, +xul|button:not([disabled="true"]):hover:active, +xul|colorpicker[type="button"]:not([disabled="true"]):hover:active, +xul|menulist[open="true"]:not([disabled="true"]) { + background-color: #dadada; +} + +html|button:disabled, +xul|button[disabled="true"], +xul|colorpicker[type="button"][disabled="true"], +xul|menulist[disabled="true"] { + cursor: not-allowed; + opacity: 0.5; +} + +*|button.primary { + background-color: #0095dd; + border-color: transparent; + color: #fff; +} + +html|button.primary:enabled:hover, +xul|button.primary:not([disabled="true"]):hover { + background-color: #008acb; +} + +html|button.primary:enabled:hover:active, +xul|button.primary:not([disabled="true"]):hover:active { + background-color: #006b9d; +} + +xul|colorpicker[type="button"] { + padding: 6px; + width: 50px; +} + +xul|button > xul|*.button-box, +xul|menulist > xul|*.menulist-label-box { + padding-right: 10px !important; + padding-left: 10px !important; +} + +xul|menulist > xul|*.menulist-label-box > xul|*.menulist-icon[src] { + -webkit-margin-end: 5px; +} + +xul|button[type="menu"] > xul|*.button-box > xul|*.button-menu-dropmarker { + -webkit-appearance: none; + margin: 1px 0; + -webkit-margin-start: 10px; + padding: 0; + width: 10px; + height: 16px; + border: none; + background-color: transparent; + list-style-image: url("chrome://global/skin/in-content/dropdown.svg#dropdown"); +} + +xul|*.help-button { + min-width: 30px; + border-radius: 2px; + border-width: 0; + background-color: #ffcb00; + background-image: none; + box-shadow: none; + list-style-image: url("chrome://global/skin/in-content/help-glyph.svg"); +} + +xul|*.help-button:not([disabled="true"]):hover { + background-color: #f4c200; + background-image: none; +} + +xul|*.help-button:not([disabled="true"]):hover:active { + background-color: #eaba00; + background-image: none; +} + +xul|*.close-icon > xul|*.button-box, +xul|*.help-button > xul|*.button-box { + padding-top: 0; + padding-bottom: 0; + padding-right: 0 !important; + padding-left: 0 !important; +} + +xul|*.help-button > xul|*.button-box > xul|*.button-icon { + width: 18px; + height: 18px; +} + +xul|*.help-button > xul|*.button-box > xul|*.button-text { + display: none; +} + +xul|*.spinbuttons-button { + -webkit-margin-start: 10px !important; + -webkit-margin-end: 2px !important; +} + +xul|*.spinbuttons-up { + margin-top: 2px !important; + border-radius: 1px 1px 0 0; +} + +xul|*.spinbuttons-down { + margin-bottom: 2px !important; + border-radius: 0 0 1px 1px; +} + +xul|*.spinbuttons-button > xul|*.button-box { + padding: 1px 5px 2px !important; +} + +xul|*.spinbuttons-up > xul|*.button-box > xul|*.button-icon { + list-style-image: url("chrome://global/skin/arrow/arrow-up.gif"); +} + +xul|*.spinbuttons-up[disabled="true"] > xul|*.button-box > xul|*.button-icon { + list-style-image: url("chrome://global/skin/arrow/arrow-up-dis.gif"); +} + +xul|*.spinbuttons-down > xul|*.button-box > xul|*.button-icon { + list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif"); +} + +xul|*.spinbuttons-down[disabled="true"] > xul|*.button-box > xul|*.button-icon { + list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.gif"); +} + +xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker { + -webkit-appearance: none; + -webkit-margin-end: 4px; + padding: 0; + border: none; + background-color: transparent; + list-style-image: url("chrome://global/skin/in-content/dropdown.svg#dropdown"); +} + +xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker > xul|*.dropmarker-icon { + width: 18px; + height: 18px; +} + +xul|menulist[disabled="true"]:not([editable="true"]) > xul|*.menulist-dropmarker { + list-style-image: url("chrome://global/skin/in-content/dropdown.svg#dropdown-disabled") +} + +xul|menulist > xul|menupopup, +xul|button[type="menu"] > xul|menupopup { + -webkit-appearance: none; + border: 1px solid #c1c1c1; + border-radius: 2px; + background-color: #fff; +} + +xul|menulist > xul|menupopup xul|menu, +xul|menulist > xul|menupopup xul|menuitem, +xul|button[type="menu"] > xul|menupopup xul|menu, +xul|button[type="menu"] > xul|menupopup xul|menuitem { + -webkit-appearance: none; + font-size: 1em; + color: #333; + padding-top: 0.2em; + padding-bottom: 0.2em; + -webkit-padding-start: 10px; + -webkit-padding-end: 30px; +} + +xul|menulist > xul|menupopup > xul|menu:not([disabled="true"])[_moz-menuactive="true"], +xul|menulist > xul|menupopup > xul|menuitem:not([disabled="true"])[_moz-menuactive="true"], +xul|button[type="menu"] > xul|menupopup > xul|menu:not([disabled="true"])[_moz-menuactive="true"], +xul|button[type="menu"] > xul|menupopup > xul|menuitem:not([disabled="true"])[_moz-menuactive="true"] { + color: #333; + background-color: rgba(0,149,221,0.25); +} + +xul|menulist > xul|menupopup > xul|menu:not([disabled="true"])[selected="true"], +xul|menulist > xul|menupopup > xul|menuitem:not([disabled="true"])[selected="true"], +xul|button[type="menu"] > xul|menupopup > xul|menu:not([disabled="true"])[selected="true"], +xul|button[type="menu"] > xul|menupopup > xul|menuitem:not([disabled="true"])[selected="true"] { + color: #fff; + background-color: #0095dd; +} + +xul|menulist > xul|menupopup > xul|menu[disabled="true"], +xul|menulist > xul|menupopup > xul|menuitem[disabled="true"], +xul|button[type="menu"] > xul|menupopup > xul|menu[disabled="true"], +xul|button[type="menu"] > xul|menupopup > xul|menuitem[disabled="true"] { + color: #999; + /* override the [_moz-menuactive="true"] background color from + global/menu.css */ + background-color: transparent; +} + +xul|menulist > xul|menupopup xul|menuseparator, +xul|button[type="menu"] > xul|menupopup xul|menuseparator { + -webkit-appearance: none; + margin: 0; + padding: 0; + border-top: 1px solid #c1c1c1; + border-bottom: none; +} + +/* textboxes */ + +*|textbox { + -webkit-appearance: none; + height: 30px; + color: #333; + line-height: 20px; + padding-right: 10px; + padding-left: 10px; + border: 1px solid #c1c1c1; + -webkit-border-top-colors: none !important; + -webkit-border-right-colors: none !important; + -webkit-border-bottom-colors: none !important; + -webkit-border-left-colors: none !important; + border-radius: 2px; + background-color: #fff; +} + +html|textbox:focus, +xul|textbox[focused] { + border-color: #0095dd; +} + +html|textbox:disabled, +xul|textbox[disabled="true"] { + opacity: 0.5; +} + +/* Links */ + +html|a, +.text-link, +.inline-link { + color: #0095dd; + text-decoration: none; +} + +html|a:hover, +.text-link:hover, +.inline-link:hover { + color: #178ce5; + text-decoration: underline; +} + +html|a:visited { + color: #551a8b; +} + +html|a:hover:active, +.text-link:hover:active, +.inline-link:hover:active { + color: #ff9500; + text-decoration: none; +} + +/* Checkboxes and radio buttons */ + +/* Hide the actual checkbox */ +html|input[type="checkbox"] { + opacity: 0; + position: absolute; +} + +/* Create a box to style as the checkbox */ +html|input[type="checkbox"] + html|label:before { + display: inline-block; + content: ""; + vertical-align: middle; +} + +html|input[type="checkbox"] + html|label { + line-height: 0px; +} + +xul|checkbox { + -webkit-margin-start: 0; +} + +xul|*.checkbox-check, +html|input[type="checkbox"] + html|label:before { + -webkit-appearance: none; + width: 23px; + height: 23px; + border-radius: 2px; + border: 1px solid #c1c1c1; + -webkit-margin-end: 10px; + background-color: #f1f1f1; + /* !important needed to override toolkit checked !important rule */ + background-image: linear-gradient(#fff, rgba(255,255,255,0.8)) !important; + background-position: center center; + background-repeat: no-repeat; + box-shadow: 0 1px 1px 0 #fff, inset 0 2px 0 0 rgba(0,0,0,0.03); +} + +xul|checkbox:not([disabled="true"]):hover > xul|*.checkbox-check, +html|input[type="checkbox"]:not(:disabled) + html|label:hover:before { + border-color: #0095dd; +} + +xul|*.checkbox-check[checked] { + list-style-image: url("chrome://global/skin/in-content/check.svg#check"); +} + +html|input[type="checkbox"]:checked + html|label:before { + background-image: url("chrome://global/skin/in-content/check.svg#check"), linear-gradient(#fff, rgba(255,255,255,0.8)) !important; +} + +xul|checkbox[disabled="true"] > xul|*.checkbox-check, +html|input[type="checkbox"]:disabled + html|label { + opacity: 0.5; +} + +xul|*.checkbox-label-box { + -webkit-margin-start: -1px; /* negative margin for the transparent border */ + -webkit-padding-start: 0; +} + +xul|richlistitem > xul|*.checkbox-check { + margin: 3px 6px; +} + +xul|radio { + -webkit-margin-start: 0; +} + +xul|*.radio-check { + -webkit-appearance: none; + width: 23px; + height: 23px; + border: 1px solid #c1c1c1; + border-radius: 50%; + -webkit-margin-end: 10px; + background-color: #f1f1f1; + background-image: linear-gradient(#fff, rgba(255,255,255,0.80)); + box-shadow: 0 1px 1px 0 #fff, inset 0 2px 0 0 rgba(0,0,0,0.03); +} + +xul|radio:not([disabled="true"]):hover > xul|*.radio-check { + border-color: #0095dd; +} + +xul|*.radio-check[selected] { + list-style-image: url("chrome://global/skin/in-content/radio.svg#radio"); +} + +xul|radio[disabled="true"] > xul|*.radio-check { + opacity: 0.5; +} + +xul|*.radio-label-box { + -webkit-margin-start: -1px; /* negative margin for the transparent border */ + -webkit-margin-end: 10px; + -webkit-padding-start: 0; +} + +/* Category List */ + +xul|*#categories { + -webkit-appearance: none; + background-color: #424f5a; + padding-top: 39px; + margin: 0; + border-width: 0; +} + +xul|*.category { + -webkit-appearance: none; + color: #c1c1c1; + -webkit-border-end-width: 0; + -webkit-padding-start: 15px; + -webkit-padding-end: 21px; + min-height: 40px; + transition: background-color 150ms; +} + +xul|*.category:hover { + background-color: #5e6972; +} + +xul|*.category[selected] { + background-color: #343f48; + color: #f2f2f2; + -webkit-padding-start: 11px; /* compensate the 4px border */ + -webkit-border-start: solid 4px #ff9500; +} + +xul|*#categories[keyboard-navigation="true"]:-webkit-focusring > xul|*.category[current] { + border-top: 1px #ffffff dotted; + border-bottom: 1px #ffffff dotted; +} + +*|*.category-name { + line-height: 22px; + font-size: 1.25rem; + padding-bottom: 2px; + -webkit-padding-start: 9px; + margin: 0; + -webkit-user-select: none; +} + +*|*.category-icon { + width: 24px; + height: 24px; +} + +/* header */ + +*|*.header { + border-bottom: 1px solid #c8c8c8; + -webkit-margin-end: 4px; /* add the 4px end-margin of other elements */ + margin-bottom: 15px; + padding-bottom: 15px; +} + +*|*.header-name { + font-size: 2.5rem; + font-weight: normal; + line-height: 40px; + margin: 0; + -webkit-user-select: none; +} + +/* File fields */ + +xul|filefield { + -webkit-appearance: none; + background-color: transparent; + border: none; + padding: 0; +} + +xul|*.fileFieldContentBox { + background-color: transparent; +} + +xul|*.fileFieldIcon { + -webkit-margin-start: 10px; + -webkit-margin-end: 0; +} + +xul|*.fileFieldLabel { + -webkit-margin-start: -26px; + -webkit-padding-start: 36px; +} + +xul|textbox:-webkit-locale-dir(rtl), +xul|*.fileFieldLabel:-webkit-locale-dir(rtl), +xul|textbox + xul|button:-webkit-locale-dir(ltr), +xul|filefield + xul|button:-webkit-locale-dir(ltr) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +xul|textbox:-webkit-locale-dir(ltr), +xul|*.fileFieldLabel:-webkit-locale-dir(ltr), +xul|textbox + xul|button:-webkit-locale-dir(rtl), +xul|filefield + xul|button:-webkit-locale-dir(rtl) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +xul|textbox + xul|button, +xul|filefield + xul|button { + -webkit-border-start: none; +} + +/* List boxes */ + +xul|richlistbox, +xul|listbox { + -webkit-appearance: none; + -webkit-margin-start: 0; + background-color: #fff; + border: 1px solid #c1c1c1; + color: #333; +} + +xul|treechildren::-webkit-tree-row, +xul|listbox xul|listitem { + padding: 0.3em; + margin: 0; + border: none; + border-radius: 0; + background-image: none; +} + +xul|treechildren::-webkit-tree-row(hover), +xul|listbox xul|listitem:hover { + background-color: rgba(0,149,221,0.25); +} + +xul|treechildren::-webkit-tree-row(selected), +xul|listbox xul|listitem[selected="true"] { + background-color: #0095dd; + color: #fff; +} + +/* Trees */ + +xul|tree { + -webkit-appearance: none; + font-size: 1em; + border: 1px solid #c1c1c1; + margin: 0; +} + +xul|tree:-webkit-focusring, +xul|richlistbox:-webkit-focusring { + border: 1px dotted #0095dd; +} + +xul|listheader, +xul|treecols { + -webkit-appearance: none; + border: none; + border-bottom: 1px solid #c1c1c1; + padding: 0; +} + +xul|treecol:not([hideheader="true"]), +xul|treecolpicker { + -webkit-appearance: none; + border: none; + background-color: #ebebeb; + color: #808080; + padding: 5px 10px; +} + +xul|treecol:not([hideheader="true"]):not([sortable="false"]):hover, +xul|treecolpicker:hover { + background-color: #dadada; + color: #333; +} + +xul|treecol:not([hideheader="true"]):not(:first-child), +xul|treecolpicker { + -webkit-border-start-width: 1px; + -webkit-border-start-style: solid; + border-image: linear-gradient(transparent 0%, transparent 20%, #c1c1c1 20%, #c1c1c1 80%, transparent 80%, transparent 100%) 1 1; +} + +xul|treecol:not([hideheader="true"]) > xul|*.treecol-sortdirection[sortDirection] { + list-style-image: url("chrome://global/skin/in-content/dropdown.svg#dropdown"); + width: 18px; + height: 18px; +} + +xul|treecol:not([hideheader="true"]) > xul|*.treecol-sortdirection[sortDirection="ascending"] { + transform: scaleY(-1); +} + +/* This is the only way to increase the height of a tree row unfortunately */ +xul|treechildren::-webkit-tree-row { + min-height: 2em; +} + +/* Color needs to be set on tree cell in order to be applied */ +xul|treechildren::-webkit-tree-cell-text { + color: #333; +} + +xul|treechildren::-webkit-tree-cell-text(selected) { + color: #fff; +} + +xul|caption { + background-color: transparent; +} + +xul|button, +html|button, +xul|colorpicker[type="button"], +xul|menulist { + margin: 2px 4px; +} + +xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker { + margin-top: 1px; + margin-bottom: 1px; +} + +xul|checkbox { + -webkit-padding-start: 0; +} + +@media not all and (-webkit-windows-default-theme) { + xul|*.checkbox-check { + background-image: none !important; + } + + xul|*.checkbox-check[checked] { + list-style-image: url("chrome://global/skin/in-content/check.svg#check-native"); + background-color: -webkit-dialog; + } +} + +xul|radio { + -webkit-binding: url("chrome://global/content/bindings/radio.xml#radio"); + -webkit-padding-start: 0; +} + +@media not all and (-webkit-windows-default-theme) { + xul|*.radio-check { + background-image: none; + } + + xul|*.radio-check[selected] { + list-style-image: url("chrome://global/skin/in-content/radio.svg#radio-native"); + background-color: -webkit-dialog; + } +} + +xul|*.radio-icon, +xul|*.checkbox-icon { + -webkit-margin-end: 0; +} + +/* Never draw a border for the focusring, use outline instead */ +xul|*.button-box, +xul|*.menulist-label-box, +xul|*.radio-label-box, +xul|*.checkbox-label-box { + border-style: none; +} + +xul|*.inline-link:-webkit-focusring, +xul|button:-webkit-focusring > xul|*.button-box, +xul|menulist:-webkit-focusring > xul|*.menulist-label-box, +xul|radio[focused="true"] > xul|*.radio-label-box, +xul|checkbox:-webkit-focusring > xul|*.checkbox-label-box { + outline: 1px dotted; +} + +/* Use a 2px border so that selected row highlight is still visible behind + an existing high-contrast border that uses the background color */ +@media not all and (-webkit-windows-default-theme) { + xul|treechildren::-webkit-tree-row(selected), + xul|listbox xul|listitem[selected="true"] { + border: 2px dotted Highlight; + } +} diff --git a/src/bin/storage/errors/notFound.html b/src/bin/storage/errors/notFound.html new file mode 100644 index 0000000..5d5cc9a --- /dev/null +++ b/src/bin/storage/errors/notFound.html @@ -0,0 +1,62 @@ + + + File not found + + + + + + + + + +
+ + +
+

File not found

+
+ + +
+ + +
+

SharpBrowser can't find the file!

+
+ + +
+
    +
  • Check the file name for capitalization or other typing errors.
  • +
  • Check to see if the file was moved, renamed or deleted.
  • +
+
+ + + + +
+ + + + +
+ + + + + \ No newline at end of file diff --git a/src/bin/storage/errors/warning-16.png b/src/bin/storage/errors/warning-16.png new file mode 100644 index 0000000..513c954 Binary files /dev/null and b/src/bin/storage/errors/warning-16.png differ diff --git a/src/bin/widevinecdmadapter.dll b/src/bin/widevinecdmadapter.dll new file mode 100644 index 0000000..ef9f9ca Binary files /dev/null and b/src/bin/widevinecdmadapter.dll differ