├── LICENSE ├── OpenSource.png ├── README.md ├── images ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── error1.png ├── error2.png ├── logo3.png └── search.png └── src ├── App.config ├── Data └── JSON.cs ├── Handlers ├── ContextMenuHandler.cs ├── DownloadHandler.cs ├── HostHandler.cs ├── KeyboardHandler.cs ├── LifeSpanHandler.cs ├── RequestHandler.cs ├── SchemeHandler.cs └── SchemeHandlerFactory.cs ├── MainForm.Designer.cs ├── MainForm.cs ├── MainForm.resx ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── Resources ├── blankpage.png └── sharpbrowser.ico ├── SharpBrowser.csproj ├── SharpBrowser.sln ├── SharpBrowser.zip ├── Utils ├── FileIconUtils.cs ├── MiscUtils.cs └── URLUtils.cs ├── obj └── Debug │ ├── DesignTimeResolveAssemblyReferencesInput.cache │ ├── SharpBrowser.MainForm.resources │ ├── SharpBrowser.Properties.Resources.resources │ ├── SharpBrowser.csproj.FileListAbsolute.txt │ ├── SharpBrowser.csproj.GenerateResource.Cache │ ├── SharpBrowser.csprojResolveAssemblyReference.cache │ ├── TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs │ ├── TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs │ └── TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs └── packages.config /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 sharpbrowser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OpenSource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/OpenSource.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build OpenSource](OpenSource.png)](https://github.com/sharpbrowser/SharpBrowser) 2 | 3 | ## ***``Windows OS Desktop PC``*** [![Build Status](https://ci.appveyor.com/api/projects/status/um3h4xqen7vql7o2/branch/master?svg=true)](https://github.com/sharpbrowser/SharpBrowser) 4 | *** 5 | ## The Prerequisites are Required: Loading Preview 6 | ## **`Microsoft .NET Framework 4.7`** 7 | [![Build Downloader](https://raw.githubusercontent.com/CreateDownloader/KugouDownloader/master/Download.PNG)](https://github.com/CreateBrowser/SharpBrowser/releases/tag/v1.0) 8 | 9 | 10 | *** 11 | ## **`NOT Microsoft .NET Framework 4.5.2`** 12 | 13 | *** 14 | 15 | ![SharpBrowser](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/logo3.png) 16 | 17 | SharpBrowser is the fastest open source C# web browser there is! Slightly faster than Google Chrome when rendering web pages due to lightweight CEF renderer. We compared every available .NET browsing browsing engine and finally settled on the high-performance [CefSharp](https://github.com/cefsharp/CefSharp/). Released under the permissive MIT license. 18 | 19 | ## Features 20 | 21 | - HTML5, CSS3, JS, HTML5 Video, WebGL 3D, etc 22 | - Tabbed browsing 23 | - Address bar (also opens Google) 24 | - Back, Forward, Stop, Refresh 25 | - Developer tools 26 | - Search bar (also highlights all instances) 27 | - Download manager 28 | - Custom error pages 29 | - Custom context menu 30 | - Easily add vendor-specific branding, buttons or hotkeys 31 | - View online & offline webpages 32 | 33 | ## Hotkeys 34 | 35 | Hotkeys | Function 36 | ------------ | ------------- 37 | Ctrl+T | Add a new tab 38 | Ctrl+N | Add a new window 39 | Ctrl+W | Close active tab 40 | F5 | Refresh active tab 41 | F12 | Open developer tools 42 | Ctrl+Tab | Switch to the next tab 43 | Ctrl+Shift+Tab | Switch to the previous tab 44 | Ctrl+F | Open search bar (Enter to find next, Esc to close) 45 | 46 | ## Code 47 | 48 | - SharpBrowser uses CefSharp 51, NET Framework 4.5.2 49 | - `MainForm.cs` - main web browser UI and related functionality 50 | - `Handlers` - various handlers that we have registered with CefSharp that enable deeper integration between us and CefSharp 51 | - `Data/JSON.cs` - fast JSON serializer/deserializer 52 | - `bin` - Binaries are included in the `bin` folder due to the complex CefSharp setup required. Don't empty this folder. 53 | - `bin/storage` - HTML and JS required for downloads manager and custom error pages 54 | 55 | ## Credits 56 | - [Mohammed Osama Mohamed Sayed Ahmed](https://github.com/mohamedosama914) - **`Microsoft .NET Framework 4.7`** SharpBrowser project. 57 | 58 | - [Robin Rodricks](https://github.com/robinrodricks) - SharpBrowser project. 59 | - [Alex Maitland](https://github.com/amaitland) - CefSharp project, wrapper for CEF embeddable browser. 60 | - [Ahmet Uzun](https://github.com/postacik) - Original browser project. 61 | 62 | ## Screenshots 63 | 64 | 65 | ### Apple Homepage 66 | 67 | ![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/1.png) 68 | 69 | ### Google Maps 70 | 71 | ![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/2.png) 72 | 73 | ### Search Bar 74 | 75 | ![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/search.png) 76 | 77 | ### Downloads Tab 78 | 79 | ![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/3.png) 80 | 81 | ### Developer Tools 82 | 83 | ![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/4.png) 84 | 85 | ### Custom Error Pages 86 | 87 | ![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/error1.png) 88 | 89 | ![](https://github.com/sharpbrowser/SharpBrowser/raw/master/images/error2.png) 90 | 91 | -------------------------------------------------------------------------------- /images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/images/1.png -------------------------------------------------------------------------------- /images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/images/2.png -------------------------------------------------------------------------------- /images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/images/3.png -------------------------------------------------------------------------------- /images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/images/4.png -------------------------------------------------------------------------------- /images/error1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/images/error1.png -------------------------------------------------------------------------------- /images/error2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/images/error2.png -------------------------------------------------------------------------------- /images/logo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/images/logo3.png -------------------------------------------------------------------------------- /images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/images/search.png -------------------------------------------------------------------------------- /src/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Data/JSON.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Data; 5 | using System.Globalization; 6 | using System.IO; 7 | using System.Text; 8 | using System.Reflection.Emit; 9 | using System.Reflection; 10 | using System.Data; 11 | using System.Xml; 12 | 13 | 14 | #region JSON 15 | 16 | public delegate string Serialize(object data); 17 | public delegate object Deserialize(string data); 18 | 19 | public class JSONParameters 20 | { 21 | /// 22 | /// Use the optimized fast Dataset Schema format (dfault = True) 23 | /// 24 | public bool UseOptimizedDatasetSchema = true; 25 | /// 26 | /// Use the fast GUID format (default = True) 27 | /// 28 | public bool UseFastGuid = true; 29 | /// 30 | /// Serialize null values to the output (default = True) 31 | /// 32 | public bool SerializeNullValues = true; 33 | /// 34 | /// Use the UTC date format (default = True) 35 | /// 36 | public bool UseUTCDateTime = true; 37 | /// 38 | /// Show the readonly properties of types in the output (default = False) 39 | /// 40 | public bool ShowReadOnlyProperties = false; 41 | /// 42 | /// Use the $types extension to optimise the output json (default = True) 43 | /// 44 | public bool UsingGlobalTypes = true; 45 | /// 46 | /// 47 | /// 48 | public bool IgnoreCaseOnDeserialize = false; 49 | /// 50 | /// Anonymous types have read only properties 51 | /// 52 | public bool EnableAnonymousTypes = false; 53 | /// 54 | /// Enable fastJSON extensions $types, $type, $map (default = True) 55 | /// 56 | public bool UseExtensions = true; 57 | } 58 | 59 | public class JSON 60 | { 61 | public readonly static JSON Instance = new JSON(); 62 | 63 | private JSON() 64 | { 65 | } 66 | /// 67 | /// You can set these paramters globally for all calls 68 | /// 69 | public JSONParameters Parameters = new JSONParameters(); 70 | private JSONParameters _params; 71 | // FIX : extensions off should not output $types 72 | public string ToJSON(object obj) 73 | { 74 | _params = Parameters; 75 | Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; 76 | return ToJSON(obj, Parameters); 77 | } 78 | 79 | public string ToJSON(object obj, JSONParameters param) 80 | { 81 | _params = param; 82 | Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; 83 | // FEATURE : enable extensions when you can deserialize anon types 84 | if (_params.EnableAnonymousTypes) { _params.UseExtensions = false; _params.UsingGlobalTypes = false; } 85 | return new JSONSerializer(param).ConvertToJSON(obj); 86 | } 87 | 88 | public object Parse(string json) 89 | { 90 | _params = Parameters; 91 | Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; 92 | return new JsonParser(json, Parameters.IgnoreCaseOnDeserialize).Decode(); 93 | } 94 | 95 | public T ToObject(string json) 96 | { 97 | return (T)ToObject(json, typeof(T)); 98 | } 99 | 100 | public object ToObject(string json) 101 | { 102 | return ToObject(json, null); 103 | } 104 | 105 | public object ToObject(string json, Type type) 106 | { 107 | _params = Parameters; 108 | Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; 109 | Dictionary ht = new JsonParser(json, Parameters.IgnoreCaseOnDeserialize).Decode() as Dictionary; 110 | if (ht == null) return null; 111 | return ParseDictionary(ht, null, type, null); 112 | } 113 | 114 | public string Beautify(string input) 115 | { 116 | return Formatter.PrettyPrint(input); 117 | } 118 | 119 | public object FillObject(object input, string json) 120 | { 121 | _params = Parameters; 122 | Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; 123 | Dictionary ht = new JsonParser(json, Parameters.IgnoreCaseOnDeserialize).Decode() as Dictionary; 124 | if (ht == null) return null; 125 | return ParseDictionary(ht, null, input.GetType(), input); 126 | } 127 | 128 | public object DeepCopy(object obj) 129 | { 130 | return ToObject(ToJSON(obj)); 131 | } 132 | 133 | public T DeepCopy(T obj) 134 | { 135 | return ToObject(ToJSON(obj)); 136 | } 137 | 138 | #region [ JSON specific reflection ] 139 | 140 | private struct myPropInfo 141 | { 142 | public bool filled; 143 | public Type pt; 144 | public Type bt; 145 | public Type changeType; 146 | public bool isDictionary; 147 | public bool isValueType; 148 | public bool isGenericType; 149 | public bool isArray; 150 | public bool isByteArray; 151 | public bool isGuid; 152 | public bool isDataSet; 153 | public bool isDataTable; 154 | public bool isHashtable; 155 | public Reflection.GenericSetter setter; 156 | public bool isEnum; 157 | public bool isDateTime; 158 | public Type[] GenericTypes; 159 | public bool isInt; 160 | public bool isLong; 161 | public bool isString; 162 | public bool isBool; 163 | public bool isClass; 164 | public Reflection.GenericGetter getter; 165 | public bool isStringDictionary; 166 | public string Name; 167 | public bool CanWrite; 168 | } 169 | 170 | SafeDictionary> _propertycache = new SafeDictionary>(); 171 | private SafeDictionary Getproperties(Type type, string typename) 172 | { 173 | SafeDictionary sd = null; 174 | if (_propertycache.TryGetValue(typename, out sd)) 175 | { 176 | return sd; 177 | } 178 | else 179 | { 180 | sd = new SafeDictionary(); 181 | PropertyInfo[] pr = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); 182 | foreach (PropertyInfo p in pr) 183 | { 184 | myPropInfo d = CreateMyProp(p.PropertyType, p.Name); 185 | d.CanWrite = p.CanWrite; 186 | d.setter = Reflection.CreateSetMethod(type, p); 187 | d.getter = Reflection.CreateGetMethod(type, p); 188 | sd.Add(p.Name, d); 189 | } 190 | FieldInfo[] fi = type.GetFields(BindingFlags.Public | BindingFlags.Instance); 191 | foreach (FieldInfo f in fi) 192 | { 193 | myPropInfo d = CreateMyProp(f.FieldType, f.Name); 194 | d.setter = Reflection.CreateSetField(type, f); 195 | d.getter = Reflection.CreateGetField(type, f); 196 | sd.Add(f.Name, d); 197 | } 198 | 199 | _propertycache.Add(typename, sd); 200 | return sd; 201 | } 202 | } 203 | 204 | private myPropInfo CreateMyProp(Type t, string name) 205 | { 206 | myPropInfo d = new myPropInfo(); 207 | d.filled = true; 208 | d.CanWrite = true; 209 | d.pt = t; 210 | d.Name = name; 211 | d.isDictionary = t.Name.Contains("Dictionary"); 212 | if (d.isDictionary) 213 | d.GenericTypes = t.GetGenericArguments(); 214 | d.isValueType = t.IsValueType; 215 | d.isGenericType = t.IsGenericType; 216 | d.isArray = t.IsArray; 217 | if (d.isArray) 218 | d.bt = t.GetElementType(); 219 | if (d.isGenericType) 220 | d.bt = t.GetGenericArguments()[0]; 221 | d.isByteArray = t == typeof(byte[]); 222 | d.isGuid = (t == typeof(Guid) || t == typeof(Guid?)); 223 | d.isHashtable = t == typeof(Hashtable); 224 | d.isDataSet = t == typeof(DataSet); 225 | d.isDataTable = t == typeof(DataTable); 226 | 227 | d.changeType = GetChangeType(t); 228 | d.isEnum = t.IsEnum; 229 | d.isDateTime = t == typeof(DateTime) || t == typeof(DateTime?); 230 | d.isInt = t == typeof(int) || t == typeof(int?); 231 | d.isLong = t == typeof(long) || t == typeof(long?); 232 | d.isString = t == typeof(string); 233 | d.isBool = t == typeof(bool) || t == typeof(bool?); 234 | d.isClass = t.IsClass; 235 | 236 | if (d.isDictionary && d.GenericTypes.Length > 0 && d.GenericTypes[0] == typeof(string)) 237 | d.isStringDictionary = true; 238 | 239 | return d; 240 | } 241 | 242 | private object ChangeType(object value, Type conversionType) 243 | { 244 | if (conversionType == typeof(int)) 245 | return (int)CreateLong((string)value); 246 | 247 | else if (conversionType == typeof(long)) 248 | return CreateLong((string)value); 249 | 250 | else if (conversionType == typeof(string)) 251 | return (string)value; 252 | 253 | else if (conversionType == typeof(Guid)) 254 | return CreateGuid((string)value); 255 | 256 | else if (conversionType.IsEnum) 257 | return CreateEnum(conversionType, (string)value); 258 | 259 | return Convert.ChangeType(value, conversionType, CultureInfo.InvariantCulture); 260 | } 261 | #endregion 262 | 263 | #region [ p r i v a t e m e t h o d s ] 264 | bool usingglobals = false; 265 | private object ParseDictionary(Dictionary d, Dictionary globaltypes, Type type, object input) 266 | { 267 | object tn = ""; 268 | 269 | if (d.TryGetValue("$types", out tn)) 270 | { 271 | usingglobals = true; 272 | globaltypes = new Dictionary(); 273 | foreach (var kv in (Dictionary)tn) 274 | { 275 | globaltypes.Add((string)kv.Value, kv.Key); 276 | } 277 | } 278 | 279 | bool found = d.TryGetValue("$type", out tn); 280 | if (found == false && type == typeof(System.Object)) 281 | { 282 | return CreateDataset(d, globaltypes); 283 | } 284 | 285 | if (found) 286 | { 287 | if (usingglobals) 288 | { 289 | object tname = ""; 290 | if (globaltypes.TryGetValue((string)tn, out tname)) 291 | tn = tname; 292 | } 293 | type = Reflection.Instance.GetTypeFromCache((string)tn); 294 | } 295 | 296 | if (type == null) 297 | throw new Exception("Cannot determine type"); 298 | 299 | string typename = type.FullName; 300 | object o = input; 301 | if (o == null) 302 | o = Reflection.Instance.FastCreateInstance(type); 303 | SafeDictionary props = Getproperties(type, typename); 304 | foreach (string n in d.Keys) 305 | { 306 | string name = n; 307 | if (_params.IgnoreCaseOnDeserialize) name = name.ToLower(); 308 | if (name == "$map") 309 | { 310 | ProcessMap(o, props, (Dictionary)d[name]); 311 | continue; 312 | } 313 | myPropInfo pi; 314 | if (props.TryGetValue(name, out pi) == false) 315 | continue; 316 | if (pi.filled == true) 317 | { 318 | object v = d[name]; 319 | 320 | if (v != null) 321 | { 322 | object oset = null; 323 | 324 | if (pi.isInt) 325 | oset = (int)CreateLong((string)v); 326 | 327 | else if (pi.isLong) 328 | oset = CreateLong((string)v); 329 | 330 | else if (pi.isString) 331 | oset = (string)v; 332 | 333 | else if (pi.isBool) 334 | oset = (bool)v; 335 | 336 | else if (pi.isGenericType && pi.isValueType == false && pi.isDictionary == false) 337 | oset = CreateGenericList((ArrayList)v, pi.pt, pi.bt, globaltypes); 338 | 339 | else if (pi.isByteArray) 340 | oset = Convert.FromBase64String((string)v); 341 | 342 | else if (pi.isArray && pi.isValueType == false) 343 | oset = CreateArray((ArrayList)v, pi.pt, pi.bt, globaltypes); 344 | 345 | else if (pi.isGuid) 346 | oset = CreateGuid((string)v); 347 | 348 | else if (pi.isDataSet) 349 | oset = CreateDataset((Dictionary)v, globaltypes); 350 | 351 | else if (pi.isDataTable) 352 | oset = this.CreateDataTable((Dictionary)v, globaltypes); 353 | 354 | else if (pi.isStringDictionary) 355 | oset = CreateStringKeyDictionary((Dictionary)v, pi.pt, pi.GenericTypes, globaltypes); 356 | 357 | else if (pi.isDictionary || pi.isHashtable) 358 | oset = CreateDictionary((ArrayList)v, pi.pt, pi.GenericTypes, globaltypes); 359 | 360 | else if (pi.isEnum) 361 | oset = CreateEnum(pi.pt, (string)v); 362 | 363 | else if (pi.isDateTime) 364 | oset = CreateDateTime((string)v); 365 | 366 | else if (pi.isClass && v is Dictionary) 367 | oset = ParseDictionary((Dictionary)v, globaltypes, pi.pt, null); 368 | 369 | else if (pi.isValueType) 370 | oset = ChangeType(v, pi.changeType); 371 | 372 | else if (v is ArrayList) 373 | oset = CreateArray((ArrayList)v, pi.pt, typeof(object), globaltypes); 374 | else 375 | oset = v; 376 | 377 | if (pi.CanWrite) 378 | o = pi.setter(o, oset); 379 | } 380 | } 381 | } 382 | return o; 383 | } 384 | 385 | private void ProcessMap(object obj, SafeDictionary props, Dictionary dic) 386 | { 387 | foreach (KeyValuePair kv in dic) 388 | { 389 | myPropInfo p = props[kv.Key]; 390 | object o = p.getter(obj); 391 | Type t = Type.GetType((string)kv.Value); 392 | if (t == typeof(Guid)) 393 | p.setter(obj, CreateGuid((string)o)); 394 | } 395 | } 396 | 397 | private long CreateLong(string s) 398 | { 399 | long num = 0; 400 | bool neg = false; 401 | foreach (char cc in s) 402 | { 403 | if (cc == '-') 404 | neg = true; 405 | else if (cc == '+') 406 | neg = false; 407 | else 408 | { 409 | num *= 10; 410 | num += (int)(cc - '0'); 411 | } 412 | } 413 | 414 | return neg ? -num : num; 415 | } 416 | 417 | private object CreateEnum(Type pt, string v) 418 | { 419 | // TODO : optimize create enum 420 | return Enum.Parse(pt, v); 421 | } 422 | 423 | private Guid CreateGuid(string s) 424 | { 425 | if (s.Length > 30) 426 | return new Guid(s); 427 | else 428 | return new Guid(Convert.FromBase64String(s)); 429 | } 430 | 431 | private DateTime CreateDateTime(string value) 432 | { 433 | bool utc = false; 434 | // 0123456789012345678 435 | // datetime format = yyyy-MM-dd HH:mm:ss 436 | int year = (int)CreateLong(value.Substring(0, 4)); 437 | int month = (int)CreateLong(value.Substring(5, 2)); 438 | int day = (int)CreateLong(value.Substring(8, 2)); 439 | int hour = (int)CreateLong(value.Substring(11, 2)); 440 | int min = (int)CreateLong(value.Substring(14, 2)); 441 | int sec = (int)CreateLong(value.Substring(17, 2)); 442 | 443 | if (value.EndsWith("Z")) 444 | utc = true; 445 | 446 | if (_params.UseUTCDateTime == false && utc == false) 447 | return new DateTime(year, month, day, hour, min, sec); 448 | else 449 | return new DateTime(year, month, day, hour, min, sec, DateTimeKind.Utc).ToLocalTime(); 450 | } 451 | 452 | private object CreateArray(ArrayList data, Type pt, Type bt, Dictionary globalTypes) 453 | { 454 | ArrayList col = new ArrayList(); 455 | // create an array of objects 456 | foreach (object ob in data) 457 | { 458 | if (ob is IDictionary) 459 | col.Add(ParseDictionary((Dictionary)ob, globalTypes, bt, null)); 460 | else 461 | col.Add(ChangeType(ob, bt)); 462 | } 463 | return col.ToArray(bt); 464 | } 465 | 466 | 467 | private object CreateGenericList(ArrayList data, Type pt, Type bt, Dictionary globalTypes) 468 | { 469 | IList col = (IList)Reflection.Instance.FastCreateInstance(pt); 470 | // create an array of objects 471 | foreach (object ob in data) 472 | { 473 | if (ob is IDictionary) 474 | col.Add(ParseDictionary((Dictionary)ob, globalTypes, bt, null)); 475 | else if (ob is ArrayList) 476 | col.Add(((ArrayList)ob).ToArray()); 477 | else 478 | col.Add(ChangeType(ob, bt)); 479 | } 480 | return col; 481 | } 482 | 483 | private object CreateStringKeyDictionary(Dictionary reader, Type pt, Type[] types, Dictionary globalTypes) 484 | { 485 | var col = (IDictionary)Reflection.Instance.FastCreateInstance(pt); 486 | Type t1 = null; 487 | Type t2 = null; 488 | if (types != null) 489 | { 490 | t1 = types[0]; 491 | t2 = types[1]; 492 | } 493 | 494 | foreach (KeyValuePair values in reader) 495 | { 496 | var key = values.Key;//ChangeType(values.Key, t1); 497 | object val = null; 498 | if (values.Value is Dictionary) 499 | val = ParseDictionary((Dictionary)values.Value, globalTypes, t2, null); 500 | else 501 | val = ChangeType(values.Value, t2); 502 | col.Add(key, val); 503 | } 504 | 505 | return col; 506 | } 507 | 508 | private object CreateDictionary(ArrayList reader, Type pt, Type[] types, Dictionary globalTypes) 509 | { 510 | IDictionary col = (IDictionary)Reflection.Instance.FastCreateInstance(pt); 511 | Type t1 = null; 512 | Type t2 = null; 513 | if (types != null) 514 | { 515 | t1 = types[0]; 516 | t2 = types[1]; 517 | } 518 | 519 | foreach (Dictionary values in reader) 520 | { 521 | object key = values["k"]; 522 | object val = values["v"]; 523 | 524 | if (key is Dictionary) 525 | key = ParseDictionary((Dictionary)key, globalTypes, t1, null); 526 | else 527 | key = ChangeType(key, t1); 528 | 529 | if (val is Dictionary) 530 | val = ParseDictionary((Dictionary)val, globalTypes, t2, null); 531 | else 532 | val = ChangeType(val, t2); 533 | 534 | col.Add(key, val); 535 | } 536 | 537 | return col; 538 | } 539 | 540 | private Type GetChangeType(Type conversionType) 541 | { 542 | if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) 543 | return conversionType.GetGenericArguments()[0]; 544 | 545 | return conversionType; 546 | } 547 | 548 | private DataSet CreateDataset(Dictionary reader, Dictionary globalTypes) 549 | { 550 | DataSet ds = new DataSet(); 551 | ds.EnforceConstraints = false; 552 | ds.BeginInit(); 553 | 554 | // read dataset schema here 555 | ReadSchema(reader, ds, globalTypes); 556 | 557 | foreach (KeyValuePair pair in reader) 558 | { 559 | if (pair.Key == "$type" || pair.Key == "$schema") continue; 560 | 561 | ArrayList rows = (ArrayList)pair.Value; 562 | if (rows == null) continue; 563 | 564 | DataTable dt = ds.Tables[pair.Key]; 565 | ReadDataTable(rows, dt); 566 | } 567 | 568 | ds.EndInit(); 569 | 570 | return ds; 571 | } 572 | 573 | private void ReadSchema(Dictionary reader, DataSet ds, Dictionary globalTypes) 574 | { 575 | var schema = reader["$schema"]; 576 | 577 | if (schema is string) 578 | { 579 | TextReader tr = new StringReader((string)schema); 580 | ds.ReadXmlSchema(tr); 581 | } 582 | else 583 | { 584 | DatasetSchema ms = (DatasetSchema)ParseDictionary((Dictionary)schema, globalTypes, typeof(DatasetSchema), null); 585 | ds.DataSetName = ms.Name; 586 | for (int i = 0; i < ms.Info.Count; i += 3) 587 | { 588 | if (ds.Tables.Contains(ms.Info[i]) == false) 589 | ds.Tables.Add(ms.Info[i]); 590 | ds.Tables[ms.Info[i]].Columns.Add(ms.Info[i + 1], Type.GetType(ms.Info[i + 2])); 591 | } 592 | } 593 | } 594 | 595 | private void ReadDataTable(ArrayList rows, DataTable dt) 596 | { 597 | dt.BeginInit(); 598 | dt.BeginLoadData(); 599 | List guidcols = new List(); 600 | List datecol = new List(); 601 | 602 | foreach (DataColumn c in dt.Columns) 603 | { 604 | if (c.DataType == typeof(Guid) || c.DataType == typeof(Guid?)) 605 | guidcols.Add(c.Ordinal); 606 | if (_params.UseUTCDateTime && (c.DataType == typeof(DateTime) || c.DataType == typeof(DateTime?))) 607 | datecol.Add(c.Ordinal); 608 | } 609 | 610 | foreach (ArrayList row in rows) 611 | { 612 | object[] v = new object[row.Count]; 613 | row.CopyTo(v, 0); 614 | foreach (int i in guidcols) 615 | { 616 | string s = (string)v[i]; 617 | if (s != null && s.Length < 36) 618 | v[i] = new Guid(Convert.FromBase64String(s)); 619 | } 620 | if (_params.UseUTCDateTime) 621 | { 622 | foreach (int i in datecol) 623 | { 624 | string s = (string)v[i]; 625 | if (s != null) 626 | v[i] = CreateDateTime(s); 627 | } 628 | } 629 | dt.Rows.Add(v); 630 | } 631 | 632 | dt.EndLoadData(); 633 | dt.EndInit(); 634 | } 635 | 636 | DataTable CreateDataTable(Dictionary reader, Dictionary globalTypes) 637 | { 638 | var dt = new DataTable(); 639 | 640 | // read dataset schema here 641 | var schema = reader["$schema"]; 642 | 643 | if (schema is string) 644 | { 645 | TextReader tr = new StringReader((string)schema); 646 | dt.ReadXmlSchema(tr); 647 | } 648 | else 649 | { 650 | var ms = (DatasetSchema)this.ParseDictionary((Dictionary)schema, globalTypes, typeof(DatasetSchema), null); 651 | dt.TableName = ms.Info[0]; 652 | for (int i = 0; i < ms.Info.Count; i += 3) 653 | { 654 | dt.Columns.Add(ms.Info[i + 1], Type.GetType(ms.Info[i + 2])); 655 | } 656 | } 657 | 658 | foreach (var pair in reader) 659 | { 660 | if (pair.Key == "$type" || pair.Key == "$schema") 661 | continue; 662 | 663 | var rows = (ArrayList)pair.Value; 664 | if (rows == null) 665 | continue; 666 | 667 | if (!dt.TableName.Equals(pair.Key, StringComparison.InvariantCultureIgnoreCase)) 668 | continue; 669 | 670 | ReadDataTable(rows, dt); 671 | } 672 | 673 | return dt; 674 | } 675 | 676 | #endregion 677 | } 678 | 679 | #endregion 680 | 681 | 682 | 683 | 684 | 685 | #region Serializer 686 | internal class JSONSerializer 687 | { 688 | private readonly StringBuilder _output = new StringBuilder(); 689 | readonly int _MAX_DEPTH = 10; 690 | int _current_depth = 0; 691 | private Dictionary _globalTypes = new Dictionary(); 692 | private JSONParameters _params; 693 | 694 | internal JSONSerializer(JSONParameters param) 695 | { 696 | _params = param; 697 | } 698 | 699 | internal string ConvertToJSON(object obj) 700 | { 701 | WriteValue(obj); 702 | 703 | string str = ""; 704 | if (_params.UsingGlobalTypes) 705 | { 706 | StringBuilder sb = new StringBuilder(); 707 | sb.Append("\"$types\":{"); 708 | bool pendingSeparator = false; 709 | foreach (var kv in _globalTypes) 710 | { 711 | if (pendingSeparator) sb.Append(','); 712 | pendingSeparator = true; 713 | sb.Append("\""); 714 | sb.Append(kv.Key); 715 | sb.Append("\":\""); 716 | sb.Append(kv.Value); 717 | sb.Append("\""); 718 | } 719 | sb.Append("},"); 720 | str = _output.Replace("$types$", sb.ToString()).ToString(); 721 | } 722 | else 723 | str = _output.ToString(); 724 | 725 | return str; 726 | } 727 | 728 | private void WriteValue(object obj) 729 | { 730 | if (obj == null || obj is DBNull) 731 | _output.Append("null"); 732 | 733 | else if (obj is string || obj is char) 734 | WriteString(obj.ToString()); 735 | 736 | else if (obj is Guid) 737 | WriteGuid((Guid)obj); 738 | 739 | else if (obj is bool) 740 | _output.Append(((bool)obj) ? "true" : "false"); // conform to standard 741 | 742 | else if ( 743 | obj is int || obj is long || obj is double || 744 | obj is decimal || obj is float || 745 | obj is byte || obj is short || 746 | obj is sbyte || obj is ushort || 747 | obj is uint || obj is ulong 748 | ) 749 | _output.Append(((IConvertible)obj).ToString(NumberFormatInfo.InvariantInfo)); 750 | 751 | else if (obj is DateTime) 752 | WriteDateTime((DateTime)obj); 753 | 754 | else if (obj is IDictionary && obj.GetType().IsGenericType && obj.GetType().GetGenericArguments()[0] == typeof(string)) 755 | WriteStringDictionary((IDictionary)obj); 756 | 757 | else if (obj is IDictionary) 758 | WriteDictionary((IDictionary)obj); 759 | 760 | else if (obj is DataSet) 761 | WriteDataset((DataSet)obj); 762 | 763 | else if (obj is DataTable) 764 | this.WriteDataTable((DataTable)obj); 765 | 766 | else if (obj is byte[]) 767 | WriteBytes((byte[])obj); 768 | 769 | else if (obj is Array || obj is IList || obj is ICollection) 770 | WriteArray((IEnumerable)obj); 771 | 772 | else if (obj is Enum) 773 | WriteEnum((Enum)obj); 774 | 775 | else 776 | WriteObject(obj); 777 | } 778 | 779 | private void WriteEnum(Enum e) 780 | { 781 | // TODO : optimize enum write 782 | WriteStringFast(e.ToString()); 783 | } 784 | 785 | private void WriteGuid(Guid g) 786 | { 787 | if (_params.UseFastGuid == false) 788 | WriteStringFast(g.ToString()); 789 | else 790 | WriteBytes(g.ToByteArray()); 791 | } 792 | 793 | private void WriteBytes(byte[] bytes) 794 | { 795 | WriteStringFast(Convert.ToBase64String(bytes, 0, bytes.Length, Base64FormattingOptions.None)); 796 | } 797 | 798 | private void WriteDateTime(DateTime dateTime) 799 | { 800 | // datetime format standard : yyyy-MM-dd HH:mm:ss 801 | DateTime dt = dateTime; 802 | if (_params.UseUTCDateTime) 803 | dt = dateTime.ToUniversalTime(); 804 | 805 | _output.Append("\""); 806 | _output.Append(dt.Year.ToString("0000", NumberFormatInfo.InvariantInfo)); 807 | _output.Append("-"); 808 | _output.Append(dt.Month.ToString("00", NumberFormatInfo.InvariantInfo)); 809 | _output.Append("-"); 810 | _output.Append(dt.Day.ToString("00", NumberFormatInfo.InvariantInfo)); 811 | _output.Append(" "); 812 | _output.Append(dt.Hour.ToString("00", NumberFormatInfo.InvariantInfo)); 813 | _output.Append(":"); 814 | _output.Append(dt.Minute.ToString("00", NumberFormatInfo.InvariantInfo)); 815 | _output.Append(":"); 816 | _output.Append(dt.Second.ToString("00", NumberFormatInfo.InvariantInfo)); 817 | 818 | if (_params.UseUTCDateTime) 819 | _output.Append("Z"); 820 | 821 | _output.Append("\""); 822 | } 823 | private DatasetSchema GetSchema(DataTable ds) 824 | { 825 | if (ds == null) return null; 826 | 827 | DatasetSchema m = new DatasetSchema(); 828 | m.Info = new List(); 829 | m.Name = ds.TableName; 830 | 831 | foreach (DataColumn c in ds.Columns) 832 | { 833 | m.Info.Add(ds.TableName); 834 | m.Info.Add(c.ColumnName); 835 | m.Info.Add(c.DataType.ToString()); 836 | } 837 | // FEATURE : serialize relations and constraints here 838 | 839 | return m; 840 | } 841 | 842 | private DatasetSchema GetSchema(DataSet ds) 843 | { 844 | if (ds == null) return null; 845 | 846 | DatasetSchema m = new DatasetSchema(); 847 | m.Info = new List(); 848 | m.Name = ds.DataSetName; 849 | 850 | foreach (DataTable t in ds.Tables) 851 | { 852 | foreach (DataColumn c in t.Columns) 853 | { 854 | m.Info.Add(t.TableName); 855 | m.Info.Add(c.ColumnName); 856 | m.Info.Add(c.DataType.ToString()); 857 | } 858 | } 859 | // FEATURE : serialize relations and constraints here 860 | 861 | return m; 862 | } 863 | 864 | private string GetXmlSchema(DataTable dt) 865 | { 866 | using (var writer = new StringWriter()) 867 | { 868 | dt.WriteXmlSchema(writer); 869 | return dt.ToString(); 870 | } 871 | } 872 | 873 | private void WriteDataset(DataSet ds) 874 | { 875 | _output.Append('{'); 876 | if ( _params.UseExtensions) 877 | { 878 | WritePair("$schema", _params.UseOptimizedDatasetSchema ? (object)GetSchema(ds) : ds.GetXmlSchema()); 879 | _output.Append(','); 880 | } 881 | bool tablesep = false; 882 | foreach (DataTable table in ds.Tables) 883 | { 884 | if (tablesep) _output.Append(","); 885 | tablesep = true; 886 | WriteDataTableData(table); 887 | } 888 | // end dataset 889 | _output.Append('}'); 890 | } 891 | 892 | private void WriteDataTableData(DataTable table) 893 | { 894 | _output.Append('\"'); 895 | _output.Append(table.TableName); 896 | _output.Append("\":["); 897 | DataColumnCollection cols = table.Columns; 898 | bool rowseparator = false; 899 | foreach (DataRow row in table.Rows) 900 | { 901 | if (rowseparator) _output.Append(","); 902 | rowseparator = true; 903 | _output.Append('['); 904 | 905 | bool pendingSeperator = false; 906 | foreach (DataColumn column in cols) 907 | { 908 | if (pendingSeperator) _output.Append(','); 909 | WriteValue(row[column]); 910 | pendingSeperator = true; 911 | } 912 | _output.Append(']'); 913 | } 914 | 915 | _output.Append(']'); 916 | } 917 | 918 | void WriteDataTable(DataTable dt) 919 | { 920 | this._output.Append('{'); 921 | if (_params.UseExtensions) 922 | { 923 | this.WritePair("$schema", _params.UseOptimizedDatasetSchema ? (object)this.GetSchema(dt) : this.GetXmlSchema(dt)); 924 | this._output.Append(','); 925 | } 926 | 927 | WriteDataTableData(dt); 928 | 929 | // end datatable 930 | this._output.Append('}'); 931 | } 932 | bool _TypesWritten = false; 933 | private void WriteObject(object obj) 934 | { 935 | if (_params.UsingGlobalTypes == false) 936 | _output.Append('{'); 937 | else 938 | { 939 | if (_TypesWritten== false) 940 | _output.Append("{$types$"); 941 | else 942 | _output.Append("{"); 943 | } 944 | _TypesWritten = true; 945 | _current_depth++; 946 | if (_current_depth > _MAX_DEPTH) 947 | throw new Exception("Serializer encountered maximum depth of " + _MAX_DEPTH); 948 | 949 | 950 | Dictionary map = new Dictionary(); 951 | Type t = obj.GetType(); 952 | bool append = false; 953 | if (_params.UseExtensions) 954 | { 955 | if (_params.UsingGlobalTypes == false) 956 | WritePairFast("$type", Reflection.Instance.GetTypeAssemblyName(t)); 957 | else 958 | { 959 | int dt = 0; 960 | string ct = Reflection.Instance.GetTypeAssemblyName(t); 961 | if (_globalTypes.TryGetValue(ct, out dt) == false) 962 | { 963 | dt = _globalTypes.Count + 1; 964 | _globalTypes.Add(ct, dt); 965 | } 966 | WritePairFast("$type", dt.ToString()); 967 | } 968 | append = true; 969 | } 970 | 971 | List g = Reflection.Instance.GetGetters(t); 972 | foreach (var p in g) 973 | { 974 | if (append) 975 | _output.Append(','); 976 | object o = p.Getter(obj); 977 | if ((o == null || o is DBNull) && _params.SerializeNullValues == false) 978 | append = false; 979 | else 980 | { 981 | WritePair(p.Name, o); 982 | if (o != null && _params.UseExtensions) 983 | { 984 | Type tt = o.GetType(); 985 | if (tt == typeof(System.Object)) 986 | map.Add(p.Name, tt.ToString()); 987 | } 988 | append = true; 989 | } 990 | } 991 | if (map.Count > 0 && _params.UseExtensions) 992 | { 993 | _output.Append(",\"$map\":"); 994 | WriteStringDictionary(map); 995 | } 996 | _current_depth--; 997 | _output.Append('}'); 998 | _current_depth--; 999 | 1000 | } 1001 | 1002 | private void WritePairFast(string name, string value) 1003 | { 1004 | if ((value == null) && _params.SerializeNullValues == false) 1005 | return; 1006 | WriteStringFast(name); 1007 | 1008 | _output.Append(':'); 1009 | 1010 | WriteStringFast(value); 1011 | } 1012 | 1013 | private void WritePair(string name, object value) 1014 | { 1015 | if ((value == null || value is DBNull) && _params.SerializeNullValues == false) 1016 | return; 1017 | WriteStringFast(name); 1018 | 1019 | _output.Append(':'); 1020 | 1021 | WriteValue(value); 1022 | } 1023 | 1024 | private void WriteArray(IEnumerable array) 1025 | { 1026 | _output.Append('['); 1027 | 1028 | bool pendingSeperator = false; 1029 | 1030 | foreach (object obj in array) 1031 | { 1032 | if (pendingSeperator) _output.Append(','); 1033 | 1034 | WriteValue(obj); 1035 | 1036 | pendingSeperator = true; 1037 | } 1038 | _output.Append(']'); 1039 | } 1040 | 1041 | private void WriteStringDictionary(IDictionary dic) 1042 | { 1043 | _output.Append('{'); 1044 | 1045 | bool pendingSeparator = false; 1046 | 1047 | foreach (DictionaryEntry entry in dic) 1048 | { 1049 | if (pendingSeparator) _output.Append(','); 1050 | 1051 | WritePair((string)entry.Key, entry.Value); 1052 | 1053 | pendingSeparator = true; 1054 | } 1055 | _output.Append('}'); 1056 | } 1057 | 1058 | private void WriteDictionary(IDictionary dic) 1059 | { 1060 | _output.Append('['); 1061 | 1062 | bool pendingSeparator = false; 1063 | 1064 | foreach (DictionaryEntry entry in dic) 1065 | { 1066 | if (pendingSeparator) _output.Append(','); 1067 | _output.Append('{'); 1068 | WritePair("k", entry.Key); 1069 | _output.Append(","); 1070 | WritePair("v", entry.Value); 1071 | _output.Append('}'); 1072 | 1073 | pendingSeparator = true; 1074 | } 1075 | _output.Append(']'); 1076 | } 1077 | 1078 | private void WriteStringFast(string s) 1079 | { 1080 | _output.Append('\"'); 1081 | _output.Append(s); 1082 | _output.Append('\"'); 1083 | } 1084 | 1085 | private void WriteString(string s) 1086 | { 1087 | _output.Append('\"'); 1088 | 1089 | int runIndex = -1; 1090 | 1091 | for (var index = 0; index < s.Length; ++index) 1092 | { 1093 | var c = s[index]; 1094 | 1095 | if (c >= ' ' && c < 128 && c != '\"' && c != '\\') 1096 | { 1097 | if (runIndex == -1) 1098 | { 1099 | runIndex = index; 1100 | } 1101 | 1102 | continue; 1103 | } 1104 | 1105 | if (runIndex != -1) 1106 | { 1107 | _output.Append(s, runIndex, index - runIndex); 1108 | runIndex = -1; 1109 | } 1110 | 1111 | switch (c) 1112 | { 1113 | case '\t': _output.Append("\\t"); break; 1114 | case '\r': _output.Append("\\r"); break; 1115 | case '\n': _output.Append("\\n"); break; 1116 | case '"': 1117 | case '\\': _output.Append('\\'); _output.Append(c); break; 1118 | default: 1119 | _output.Append("\\u"); 1120 | _output.Append(((int)c).ToString("X4", NumberFormatInfo.InvariantInfo)); 1121 | break; 1122 | } 1123 | } 1124 | 1125 | if (runIndex != -1) 1126 | { 1127 | _output.Append(s, runIndex, s.Length - runIndex); 1128 | } 1129 | 1130 | _output.Append('\"'); 1131 | } 1132 | } 1133 | 1134 | 1135 | #endregion 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | #region Reflection 1142 | internal class Getters 1143 | { 1144 | public string Name; 1145 | public Reflection.GenericGetter Getter; 1146 | public Type propertyType; 1147 | } 1148 | 1149 | internal class Reflection 1150 | { 1151 | public readonly static Reflection Instance = new Reflection(); 1152 | private Reflection() 1153 | { 1154 | } 1155 | 1156 | public bool ShowReadOnlyProperties = false; 1157 | internal delegate object GenericSetter(object target, object value); 1158 | internal delegate object GenericGetter(object obj); 1159 | private delegate object CreateObject(); 1160 | 1161 | private SafeDictionary _tyname = new SafeDictionary(); 1162 | private SafeDictionary _typecache = new SafeDictionary(); 1163 | private SafeDictionary _constrcache = new SafeDictionary(); 1164 | private SafeDictionary> _getterscache = new SafeDictionary>(); 1165 | 1166 | #region [ PROPERTY GET SET ] 1167 | internal string GetTypeAssemblyName(Type t) 1168 | { 1169 | string val = ""; 1170 | if (_tyname.TryGetValue(t, out val)) 1171 | return val; 1172 | else 1173 | { 1174 | string s = t.AssemblyQualifiedName; 1175 | _tyname.Add(t, s); 1176 | return s; 1177 | } 1178 | } 1179 | 1180 | internal Type GetTypeFromCache(string typename) 1181 | { 1182 | Type val = null; 1183 | if (_typecache.TryGetValue(typename, out val)) 1184 | return val; 1185 | else 1186 | { 1187 | Type t = Type.GetType(typename); 1188 | _typecache.Add(typename, t); 1189 | return t; 1190 | } 1191 | } 1192 | 1193 | internal object FastCreateInstance(Type objtype) 1194 | { 1195 | try 1196 | { 1197 | CreateObject c = null; 1198 | if (_constrcache.TryGetValue(objtype, out c)) 1199 | { 1200 | return c(); 1201 | } 1202 | else 1203 | { 1204 | DynamicMethod dynMethod = new DynamicMethod("_", 1205 | MethodAttributes.Public | MethodAttributes.Static, 1206 | CallingConventions.Standard, 1207 | typeof(object), 1208 | null, 1209 | objtype, false); 1210 | ILGenerator ilGen = dynMethod.GetILGenerator(); 1211 | 1212 | if (objtype.IsClass) 1213 | { 1214 | ilGen.Emit(OpCodes.Newobj, objtype.GetConstructor(Type.EmptyTypes)); 1215 | ilGen.Emit(OpCodes.Ret); 1216 | c = (CreateObject)dynMethod.CreateDelegate(typeof(CreateObject)); 1217 | _constrcache.Add(objtype, c); 1218 | } 1219 | else // structs 1220 | { 1221 | var lv = ilGen.DeclareLocal(objtype); 1222 | ilGen.Emit(OpCodes.Ldloca_S, lv); 1223 | ilGen.Emit(OpCodes.Initobj, objtype); 1224 | ilGen.Emit(OpCodes.Ldloc_0); 1225 | ilGen.Emit(OpCodes.Box, objtype); 1226 | ilGen.Emit(OpCodes.Ret); 1227 | c = (CreateObject)dynMethod.CreateDelegate(typeof(CreateObject)); 1228 | _constrcache.Add(objtype, c); 1229 | } 1230 | return c(); 1231 | } 1232 | } 1233 | catch (Exception exc) 1234 | { 1235 | throw new Exception(string.Format("Failed to fast create instance for type '{0}' from assemebly '{1}'", 1236 | objtype.FullName, objtype.AssemblyQualifiedName), exc); 1237 | } 1238 | } 1239 | 1240 | internal static GenericSetter CreateSetField(Type type, FieldInfo fieldInfo) 1241 | { 1242 | Type[] arguments = new Type[2]; 1243 | arguments[0] = arguments[1] = typeof(object); 1244 | 1245 | DynamicMethod dynamicSet = new DynamicMethod("_", typeof(object), arguments, type, true); 1246 | ILGenerator il = dynamicSet.GetILGenerator(); 1247 | 1248 | if (!type.IsClass) // structs 1249 | { 1250 | var lv = il.DeclareLocal(type); 1251 | il.Emit(OpCodes.Ldarg_0); 1252 | il.Emit(OpCodes.Unbox_Any, type); 1253 | il.Emit(OpCodes.Stloc_0); 1254 | il.Emit(OpCodes.Ldloca_S, lv); 1255 | il.Emit(OpCodes.Ldarg_1); 1256 | if (fieldInfo.FieldType.IsClass) 1257 | il.Emit(OpCodes.Castclass, fieldInfo.FieldType); 1258 | else 1259 | il.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType); 1260 | il.Emit(OpCodes.Stfld, fieldInfo); 1261 | il.Emit(OpCodes.Ldloc_0); 1262 | il.Emit(OpCodes.Box, type); 1263 | il.Emit(OpCodes.Ret); 1264 | } 1265 | else 1266 | { 1267 | il.Emit(OpCodes.Ldarg_0); 1268 | il.Emit(OpCodes.Ldarg_1); 1269 | if (fieldInfo.FieldType.IsValueType) 1270 | il.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType); 1271 | il.Emit(OpCodes.Stfld, fieldInfo); 1272 | il.Emit(OpCodes.Ldarg_0); 1273 | il.Emit(OpCodes.Ret); 1274 | } 1275 | return (GenericSetter)dynamicSet.CreateDelegate(typeof(GenericSetter)); 1276 | } 1277 | 1278 | internal static GenericSetter CreateSetMethod(Type type, PropertyInfo propertyInfo) 1279 | { 1280 | MethodInfo setMethod = propertyInfo.GetSetMethod(); 1281 | if (setMethod == null) 1282 | return null; 1283 | 1284 | Type[] arguments = new Type[2]; 1285 | arguments[0] = arguments[1] = typeof(object); 1286 | 1287 | DynamicMethod setter = new DynamicMethod("_", typeof(object), arguments, type); 1288 | ILGenerator il = setter.GetILGenerator(); 1289 | 1290 | if (!type.IsClass) // structs 1291 | { 1292 | var lv = il.DeclareLocal(type); 1293 | il.Emit(OpCodes.Ldarg_0); 1294 | il.Emit(OpCodes.Unbox_Any, type); 1295 | il.Emit(OpCodes.Stloc_0); 1296 | il.Emit(OpCodes.Ldloca_S, lv); 1297 | il.Emit(OpCodes.Ldarg_1); 1298 | if (propertyInfo.PropertyType.IsClass) 1299 | il.Emit(OpCodes.Castclass, propertyInfo.PropertyType); 1300 | else 1301 | il.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType); 1302 | il.EmitCall(OpCodes.Call, setMethod, null); 1303 | il.Emit(OpCodes.Ldloc_0); 1304 | il.Emit(OpCodes.Box, type); 1305 | } 1306 | else 1307 | { 1308 | il.Emit(OpCodes.Ldarg_0); 1309 | il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); 1310 | il.Emit(OpCodes.Ldarg_1); 1311 | if (propertyInfo.PropertyType.IsClass) 1312 | il.Emit(OpCodes.Castclass, propertyInfo.PropertyType); 1313 | else 1314 | il.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType); 1315 | il.EmitCall(OpCodes.Callvirt, setMethod, null); 1316 | il.Emit(OpCodes.Ldarg_0); 1317 | } 1318 | 1319 | il.Emit(OpCodes.Ret); 1320 | 1321 | return (GenericSetter)setter.CreateDelegate(typeof(GenericSetter)); 1322 | } 1323 | 1324 | internal static GenericGetter CreateGetField(Type type, FieldInfo fieldInfo) 1325 | { 1326 | DynamicMethod dynamicGet = new DynamicMethod("_", typeof(object), new Type[] { typeof(object) }, type, true); 1327 | ILGenerator il = dynamicGet.GetILGenerator(); 1328 | 1329 | if (!type.IsClass) // structs 1330 | { 1331 | var lv = il.DeclareLocal(type); 1332 | il.Emit(OpCodes.Ldarg_0); 1333 | il.Emit(OpCodes.Unbox_Any, type); 1334 | il.Emit(OpCodes.Stloc_0); 1335 | il.Emit(OpCodes.Ldloca_S, lv); 1336 | il.Emit(OpCodes.Ldfld, fieldInfo); 1337 | if (fieldInfo.FieldType.IsValueType) 1338 | il.Emit(OpCodes.Box, fieldInfo.FieldType); 1339 | } 1340 | else 1341 | { 1342 | il.Emit(OpCodes.Ldarg_0); 1343 | il.Emit(OpCodes.Ldfld, fieldInfo); 1344 | if (fieldInfo.FieldType.IsValueType) 1345 | il.Emit(OpCodes.Box, fieldInfo.FieldType); 1346 | } 1347 | 1348 | il.Emit(OpCodes.Ret); 1349 | 1350 | return (GenericGetter)dynamicGet.CreateDelegate(typeof(GenericGetter)); 1351 | } 1352 | 1353 | internal static GenericGetter CreateGetMethod(Type type, PropertyInfo propertyInfo) 1354 | { 1355 | MethodInfo getMethod = propertyInfo.GetGetMethod(); 1356 | if (getMethod == null) 1357 | return null; 1358 | 1359 | Type[] arguments = new Type[1]; 1360 | arguments[0] = typeof(object); 1361 | 1362 | DynamicMethod getter = new DynamicMethod("_", typeof(object), arguments, type); 1363 | ILGenerator il = getter.GetILGenerator(); 1364 | 1365 | if (!type.IsClass) // structs 1366 | { 1367 | var lv = il.DeclareLocal(type); 1368 | il.Emit(OpCodes.Ldarg_0); 1369 | il.Emit(OpCodes.Unbox_Any, type); 1370 | il.Emit(OpCodes.Stloc_0); 1371 | il.Emit(OpCodes.Ldloca_S, lv); 1372 | il.EmitCall(OpCodes.Call, getMethod, null); 1373 | if (propertyInfo.PropertyType.IsValueType) 1374 | il.Emit(OpCodes.Box, propertyInfo.PropertyType); 1375 | } 1376 | else 1377 | { 1378 | il.Emit(OpCodes.Ldarg_0); 1379 | il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); 1380 | il.EmitCall(OpCodes.Callvirt, getMethod, null); 1381 | if (propertyInfo.PropertyType.IsValueType) 1382 | il.Emit(OpCodes.Box, propertyInfo.PropertyType); 1383 | } 1384 | 1385 | il.Emit(OpCodes.Ret); 1386 | 1387 | return (GenericGetter)getter.CreateDelegate(typeof(GenericGetter)); 1388 | } 1389 | 1390 | internal List GetGetters(Type type) 1391 | { 1392 | List val = null; 1393 | if (_getterscache.TryGetValue(type, out val)) 1394 | return val; 1395 | 1396 | PropertyInfo[] props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); 1397 | List getters = new List(); 1398 | foreach (PropertyInfo p in props) 1399 | { 1400 | if (!p.CanWrite && ShowReadOnlyProperties == false) continue; 1401 | 1402 | object[] att = p.GetCustomAttributes(typeof(System.Xml.Serialization.XmlIgnoreAttribute), false); 1403 | if (att != null && att.Length > 0) 1404 | continue; 1405 | 1406 | GenericGetter g = CreateGetMethod(type, p); 1407 | if (g != null) 1408 | { 1409 | Getters gg = new Getters(); 1410 | gg.Name = p.Name; 1411 | gg.Getter = g; 1412 | gg.propertyType = p.PropertyType; 1413 | getters.Add(gg); 1414 | } 1415 | } 1416 | 1417 | FieldInfo[] fi = type.GetFields(BindingFlags.Instance | BindingFlags.Public); 1418 | foreach (var f in fi) 1419 | { 1420 | object[] att = f.GetCustomAttributes(typeof(System.Xml.Serialization.XmlIgnoreAttribute), false); 1421 | if (att != null && att.Length > 0) 1422 | continue; 1423 | 1424 | GenericGetter g = CreateGetField(type, f); 1425 | if (g != null) 1426 | { 1427 | Getters gg = new Getters(); 1428 | gg.Name = f.Name; 1429 | gg.Getter = g; 1430 | gg.propertyType = f.FieldType; 1431 | getters.Add(gg); 1432 | } 1433 | } 1434 | 1435 | _getterscache.Add(type, getters); 1436 | return getters; 1437 | } 1438 | 1439 | #endregion 1440 | } 1441 | #endregion 1442 | 1443 | 1444 | 1445 | 1446 | 1447 | #region SafeDictionary 1448 | internal class SafeDictionary 1449 | { 1450 | private readonly object _Padlock = new object(); 1451 | private readonly Dictionary _Dictionary; 1452 | 1453 | public SafeDictionary(int capacity) 1454 | { 1455 | _Dictionary = new Dictionary(capacity); 1456 | } 1457 | 1458 | public SafeDictionary() 1459 | { 1460 | _Dictionary = new Dictionary(); 1461 | } 1462 | 1463 | public bool TryGetValue(TKey key, out TValue value) 1464 | { 1465 | lock (_Padlock) 1466 | return _Dictionary.TryGetValue(key, out value); 1467 | } 1468 | 1469 | public TValue this[TKey key] 1470 | { 1471 | get 1472 | { 1473 | lock (_Padlock) 1474 | return _Dictionary[key]; 1475 | } 1476 | set 1477 | { 1478 | lock (_Padlock) 1479 | _Dictionary[key] = value; 1480 | } 1481 | } 1482 | 1483 | public void Add(TKey key, TValue value) 1484 | { 1485 | lock (_Padlock) 1486 | { 1487 | if (_Dictionary.ContainsKey(key) == false) 1488 | _Dictionary.Add(key, value); 1489 | } 1490 | } 1491 | } 1492 | #endregion 1493 | 1494 | 1495 | 1496 | 1497 | #region Formatter 1498 | internal static class Formatter 1499 | { 1500 | public static string Indent = " "; 1501 | 1502 | public static void AppendIndent(StringBuilder sb, int count) 1503 | { 1504 | for (; count > 0; --count) sb.Append(Indent); 1505 | } 1506 | 1507 | public static bool IsEscaped(StringBuilder sb, int index) 1508 | { 1509 | bool escaped = false; 1510 | while (index > 0 && sb[--index] == '\\') escaped = !escaped; 1511 | return escaped; 1512 | } 1513 | 1514 | public static string PrettyPrint(string input) 1515 | { 1516 | var output = new StringBuilder(input.Length * 2); 1517 | char? quote = null; 1518 | int depth = 0; 1519 | 1520 | for (int i = 0; i < input.Length; ++i) 1521 | { 1522 | char ch = input[i]; 1523 | 1524 | switch (ch) 1525 | { 1526 | case '{': 1527 | case '[': 1528 | output.Append(ch); 1529 | if (!quote.HasValue) 1530 | { 1531 | output.AppendLine(); 1532 | AppendIndent(output, ++depth); 1533 | } 1534 | break; 1535 | case '}': 1536 | case ']': 1537 | if (quote.HasValue) 1538 | output.Append(ch); 1539 | else 1540 | { 1541 | output.AppendLine(); 1542 | AppendIndent(output, --depth); 1543 | output.Append(ch); 1544 | } 1545 | break; 1546 | case '"': 1547 | case '\'': 1548 | output.Append(ch); 1549 | if (quote.HasValue) 1550 | { 1551 | if (!IsEscaped(output, i)) 1552 | quote = null; 1553 | } 1554 | else quote = ch; 1555 | break; 1556 | case ',': 1557 | output.Append(ch); 1558 | if (!quote.HasValue) 1559 | { 1560 | output.AppendLine(); 1561 | AppendIndent(output, depth); 1562 | } 1563 | break; 1564 | case ':': 1565 | if (quote.HasValue) output.Append(ch); 1566 | else output.Append(" : "); 1567 | break; 1568 | default: 1569 | if (quote.HasValue || !char.IsWhiteSpace(ch)) 1570 | output.Append(ch); 1571 | break; 1572 | } 1573 | } 1574 | 1575 | return output.ToString(); 1576 | } 1577 | } 1578 | #endregion 1579 | 1580 | 1581 | 1582 | 1583 | #region Getters 1584 | public class DatasetSchema 1585 | { 1586 | public List Info { get; set; } 1587 | public string Name { get; set; } 1588 | } 1589 | #endregion 1590 | 1591 | 1592 | 1593 | 1594 | 1595 | 1596 | #region Parser 1597 | 1598 | /// 1599 | /// This class encodes and decodes JSON strings. 1600 | /// Spec. details, see http://www.json.org/ 1601 | /// 1602 | /// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable. 1603 | /// All numbers are parsed to doubles. 1604 | /// 1605 | internal class JsonParser 1606 | { 1607 | enum Token 1608 | { 1609 | None = -1, // Used to denote no Lookahead available 1610 | Curly_Open, 1611 | Curly_Close, 1612 | Squared_Open, 1613 | Squared_Close, 1614 | Colon, 1615 | Comma, 1616 | String, 1617 | Number, 1618 | True, 1619 | False, 1620 | Null 1621 | } 1622 | 1623 | readonly char[] json; 1624 | readonly StringBuilder s = new StringBuilder(); 1625 | Token lookAheadToken = Token.None; 1626 | int index; 1627 | bool _ignorecase = false; 1628 | 1629 | 1630 | internal JsonParser(string json, bool ignorecase) 1631 | { 1632 | this.json = json.ToCharArray(); 1633 | _ignorecase = ignorecase; 1634 | } 1635 | 1636 | public object Decode() 1637 | { 1638 | return ParseValue(); 1639 | } 1640 | 1641 | private Dictionary ParseObject() 1642 | { 1643 | Dictionary table = new Dictionary(); 1644 | 1645 | ConsumeToken(); // { 1646 | 1647 | while (true) 1648 | { 1649 | switch (LookAhead()) 1650 | { 1651 | 1652 | case Token.Comma: 1653 | ConsumeToken(); 1654 | break; 1655 | 1656 | case Token.Curly_Close: 1657 | ConsumeToken(); 1658 | return table; 1659 | 1660 | default: 1661 | { 1662 | 1663 | // name 1664 | string name = ParseString(); 1665 | if (_ignorecase) 1666 | name = name.ToLower(); 1667 | 1668 | // : 1669 | if (NextToken() != Token.Colon) 1670 | { 1671 | throw new Exception("Expected colon at index " + index); 1672 | } 1673 | 1674 | // value 1675 | object value = ParseValue(); 1676 | 1677 | table[name] = value; 1678 | } 1679 | break; 1680 | } 1681 | } 1682 | } 1683 | 1684 | private ArrayList ParseArray() 1685 | { 1686 | ArrayList array = new ArrayList(); 1687 | 1688 | ConsumeToken(); // [ 1689 | 1690 | while (true) 1691 | { 1692 | switch (LookAhead()) 1693 | { 1694 | 1695 | case Token.Comma: 1696 | ConsumeToken(); 1697 | break; 1698 | 1699 | case Token.Squared_Close: 1700 | ConsumeToken(); 1701 | return array; 1702 | 1703 | default: 1704 | { 1705 | array.Add(ParseValue()); 1706 | } 1707 | break; 1708 | } 1709 | } 1710 | } 1711 | 1712 | private object ParseValue() 1713 | { 1714 | switch (LookAhead()) 1715 | { 1716 | case Token.Number: 1717 | return ParseNumber(); 1718 | 1719 | case Token.String: 1720 | return ParseString(); 1721 | 1722 | case Token.Curly_Open: 1723 | return ParseObject(); 1724 | 1725 | case Token.Squared_Open: 1726 | return ParseArray(); 1727 | 1728 | case Token.True: 1729 | ConsumeToken(); 1730 | return true; 1731 | 1732 | case Token.False: 1733 | ConsumeToken(); 1734 | return false; 1735 | 1736 | case Token.Null: 1737 | ConsumeToken(); 1738 | return null; 1739 | } 1740 | 1741 | throw new Exception("Unrecognized token at index" + index); 1742 | } 1743 | 1744 | private string ParseString() 1745 | { 1746 | ConsumeToken(); // " 1747 | 1748 | s.Length = 0; 1749 | 1750 | int runIndex = -1; 1751 | 1752 | while (index < json.Length) 1753 | { 1754 | var c = json[index++]; 1755 | 1756 | if (c == '"') 1757 | { 1758 | if (runIndex != -1) 1759 | { 1760 | if (s.Length == 0) 1761 | return new string(json, runIndex, index - runIndex - 1); 1762 | 1763 | s.Append(json, runIndex, index - runIndex - 1); 1764 | } 1765 | return s.ToString(); 1766 | } 1767 | 1768 | if (c != '\\') 1769 | { 1770 | if (runIndex == -1) 1771 | runIndex = index - 1; 1772 | 1773 | continue; 1774 | } 1775 | 1776 | if (index == json.Length) break; 1777 | 1778 | if (runIndex != -1) 1779 | { 1780 | s.Append(json, runIndex, index - runIndex - 1); 1781 | runIndex = -1; 1782 | } 1783 | 1784 | switch (json[index++]) 1785 | { 1786 | case '"': 1787 | s.Append('"'); 1788 | break; 1789 | 1790 | case '\\': 1791 | s.Append('\\'); 1792 | break; 1793 | 1794 | case '/': 1795 | s.Append('/'); 1796 | break; 1797 | 1798 | case 'b': 1799 | s.Append('\b'); 1800 | break; 1801 | 1802 | case 'f': 1803 | s.Append('\f'); 1804 | break; 1805 | 1806 | case 'n': 1807 | s.Append('\n'); 1808 | break; 1809 | 1810 | case 'r': 1811 | s.Append('\r'); 1812 | break; 1813 | 1814 | case 't': 1815 | s.Append('\t'); 1816 | break; 1817 | 1818 | case 'u': 1819 | { 1820 | int remainingLength = json.Length - index; 1821 | if (remainingLength < 4) break; 1822 | 1823 | // parse the 32 bit hex into an integer codepoint 1824 | uint codePoint = ParseUnicode(json[index], json[index + 1], json[index + 2], json[index + 3]); 1825 | s.Append((char)codePoint); 1826 | 1827 | // skip 4 chars 1828 | index += 4; 1829 | } 1830 | break; 1831 | } 1832 | } 1833 | 1834 | throw new Exception("Unexpectedly reached end of string"); 1835 | } 1836 | 1837 | private uint ParseSingleChar(char c1, uint multipliyer) 1838 | { 1839 | uint p1 = 0; 1840 | if (c1 >= '0' && c1 <= '9') 1841 | p1 = (uint)(c1 - '0') * multipliyer; 1842 | else if (c1 >= 'A' && c1 <= 'F') 1843 | p1 = (uint)((c1 - 'A') + 10) * multipliyer; 1844 | else if (c1 >= 'a' && c1 <= 'f') 1845 | p1 = (uint)((c1 - 'a') + 10) * multipliyer; 1846 | return p1; 1847 | } 1848 | 1849 | private uint ParseUnicode(char c1, char c2, char c3, char c4) 1850 | { 1851 | uint p1 = ParseSingleChar(c1, 0x1000); 1852 | uint p2 = ParseSingleChar(c2, 0x100); 1853 | uint p3 = ParseSingleChar(c3, 0x10); 1854 | uint p4 = ParseSingleChar(c4, 1); 1855 | 1856 | return p1 + p2 + p3 + p4; 1857 | } 1858 | 1859 | private string ParseNumber() 1860 | { 1861 | ConsumeToken(); 1862 | 1863 | // Need to start back one place because the first digit is also a token and would have been consumed 1864 | var startIndex = index - 1; 1865 | 1866 | do 1867 | { 1868 | var c = json[index]; 1869 | 1870 | if ((c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E') 1871 | { 1872 | if (++index == json.Length) throw new Exception("Unexpected end of string whilst parsing number"); 1873 | continue; 1874 | } 1875 | 1876 | break; 1877 | } while (true); 1878 | 1879 | return new string(json, startIndex, index - startIndex); 1880 | } 1881 | 1882 | private Token LookAhead() 1883 | { 1884 | if (lookAheadToken != Token.None) return lookAheadToken; 1885 | 1886 | return lookAheadToken = NextTokenCore(); 1887 | } 1888 | 1889 | private void ConsumeToken() 1890 | { 1891 | lookAheadToken = Token.None; 1892 | } 1893 | 1894 | private Token NextToken() 1895 | { 1896 | var result = lookAheadToken != Token.None ? lookAheadToken : NextTokenCore(); 1897 | 1898 | lookAheadToken = Token.None; 1899 | 1900 | return result; 1901 | } 1902 | 1903 | private Token NextTokenCore() 1904 | { 1905 | char c; 1906 | 1907 | // Skip past whitespace 1908 | do 1909 | { 1910 | c = json[index]; 1911 | 1912 | if (c > ' ') break; 1913 | if (c != ' ' && c != '\t' && c != '\n' && c != '\r') break; 1914 | 1915 | } while (++index < json.Length); 1916 | 1917 | if (index == json.Length) 1918 | { 1919 | throw new Exception("Reached end of string unexpectedly"); 1920 | } 1921 | 1922 | c = json[index]; 1923 | 1924 | index++; 1925 | 1926 | //if (c >= '0' && c <= '9') 1927 | // return Token.Number; 1928 | 1929 | switch (c) 1930 | { 1931 | case '{': 1932 | return Token.Curly_Open; 1933 | 1934 | case '}': 1935 | return Token.Curly_Close; 1936 | 1937 | case '[': 1938 | return Token.Squared_Open; 1939 | 1940 | case ']': 1941 | return Token.Squared_Close; 1942 | 1943 | case ',': 1944 | return Token.Comma; 1945 | 1946 | case '"': 1947 | return Token.String; 1948 | 1949 | case '0': case '1': case '2': case '3': case '4': 1950 | case '5': case '6': case '7': case '8': case '9': 1951 | case '-': case '+': case '.': 1952 | return Token.Number; 1953 | 1954 | case ':': 1955 | return Token.Colon; 1956 | 1957 | case 'f': 1958 | if (json.Length - index >= 4 && 1959 | json[index + 0] == 'a' && 1960 | json[index + 1] == 'l' && 1961 | json[index + 2] == 's' && 1962 | json[index + 3] == 'e') 1963 | { 1964 | index += 4; 1965 | return Token.False; 1966 | } 1967 | break; 1968 | 1969 | case 't': 1970 | if (json.Length - index >= 3 && 1971 | json[index + 0] == 'r' && 1972 | json[index + 1] == 'u' && 1973 | json[index + 2] == 'e') 1974 | { 1975 | index += 3; 1976 | return Token.True; 1977 | } 1978 | break; 1979 | 1980 | case 'n': 1981 | if (json.Length - index >= 3 && 1982 | json[index + 0] == 'u' && 1983 | json[index + 1] == 'l' && 1984 | json[index + 2] == 'l') 1985 | { 1986 | index += 3; 1987 | return Token.Null; 1988 | } 1989 | break; 1990 | 1991 | } 1992 | 1993 | throw new Exception("Could not find token at index " + --index); 1994 | } 1995 | } 1996 | #endregion 1997 | 1998 | -------------------------------------------------------------------------------- /src/Handlers/ContextMenuHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using CefSharp; 6 | using System.Windows.Forms; 7 | using CefSharp.WinForms; 8 | 9 | namespace SharpBrowser { 10 | internal class ContextMenuHandler : IContextMenuHandler { 11 | 12 | private const int ShowDevTools = 26501; 13 | private const int CloseDevTools = 26502; 14 | private const int SaveImageAs = 26503; 15 | private const int SaveAsPdf = 26504; 16 | private const int SaveLinkAs = 26505; 17 | private const int CopyLinkAddress = 26506; 18 | private const int OpenLinkInNewTab = 26507; 19 | private const int CloseTab = 40007; 20 | private const int RefreshTab = 40008; 21 | MainForm myForm; 22 | 23 | private string lastSelText = ""; 24 | 25 | public ContextMenuHandler(MainForm form) { 26 | myForm = form; 27 | } 28 | 29 | public void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model) { 30 | 31 | // clear the menu 32 | model.Clear(); 33 | 34 | // save text 35 | lastSelText = parameters.SelectionText; 36 | 37 | // to copy text 38 | if (parameters.SelectionText.CheckIfValid()) { 39 | model.AddItem(CefMenuCommand.Copy, "Copy"); 40 | model.AddSeparator(); 41 | } 42 | 43 | //Removing existing menu item 44 | //bool removed = model.Remove(CefMenuCommand.ViewSource); // Remove "View Source" option 45 | if (parameters.LinkUrl != "") { 46 | model.AddItem((CefMenuCommand)OpenLinkInNewTab, "Open link in new tab"); 47 | model.AddItem((CefMenuCommand)CopyLinkAddress, "Copy link"); 48 | model.AddSeparator(); 49 | } 50 | 51 | if (parameters.HasImageContents && parameters.SourceUrl.CheckIfValid()) { 52 | 53 | // RIGHT CLICKED ON IMAGE 54 | 55 | } 56 | 57 | if (parameters.SelectionText != null) { 58 | 59 | // TEXT IS SELECTED 60 | 61 | } 62 | 63 | //Add new custom menu items 64 | //#if DEBUG 65 | model.AddItem((CefMenuCommand)ShowDevTools, "Developer tools"); 66 | model.AddItem(CefMenuCommand.ViewSource, "View source"); 67 | model.AddSeparator(); 68 | //#endif 69 | 70 | model.AddItem((CefMenuCommand)RefreshTab, "Refresh tab"); 71 | model.AddItem((CefMenuCommand)CloseTab, "Close tab"); 72 | 73 | } 74 | 75 | public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags) { 76 | 77 | int id = (int)commandId; 78 | 79 | if (id == ShowDevTools) { 80 | browser.ShowDevTools(); 81 | } 82 | if (id == CloseDevTools) { 83 | browser.CloseDevTools(); 84 | } 85 | if (id == SaveImageAs) { 86 | browser.GetHost().StartDownload(parameters.SourceUrl); 87 | } 88 | if (id == SaveLinkAs) { 89 | browser.GetHost().StartDownload(parameters.LinkUrl); 90 | } 91 | if (id == OpenLinkInNewTab) { 92 | ChromiumWebBrowser newBrowser = myForm.AddNewBrowserTab(parameters.LinkUrl, false, browser.MainFrame.Url); 93 | } 94 | if (id == CopyLinkAddress) { 95 | Clipboard.SetText(parameters.LinkUrl); 96 | } 97 | if (id == CloseTab) { 98 | myForm.InvokeOnParent(delegate() { 99 | myForm.CloseActiveTab(); 100 | }); 101 | } 102 | if (id == RefreshTab) { 103 | myForm.InvokeOnParent(delegate() { 104 | myForm.RefreshActiveTab(); 105 | }); 106 | } 107 | 108 | return false; 109 | } 110 | 111 | public void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame) { 112 | 113 | } 114 | 115 | public bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback) { 116 | 117 | // show default menu 118 | return false; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Handlers/DownloadHandler.cs: -------------------------------------------------------------------------------- 1 | using CefSharp; 2 | 3 | namespace SharpBrowser { 4 | internal class DownloadHandler : IDownloadHandler 5 | { 6 | MainForm myForm; 7 | 8 | public DownloadHandler(MainForm form) 9 | { 10 | myForm = form; 11 | } 12 | 13 | public void OnBeforeDownload(IBrowser browser, DownloadItem item, IBeforeDownloadCallback callback) 14 | { 15 | if (!callback.IsDisposed) 16 | { 17 | using (callback) 18 | { 19 | 20 | myForm.UpdateDownloadItem(item); 21 | 22 | // ask browser what path it wants to save the file into 23 | string path = myForm.CalcDownloadPath(item); 24 | 25 | // if file should not be saved, path will be null, so skip file 26 | if (path != null) { 27 | 28 | // skip file 29 | callback.Continue(path, false); 30 | 31 | } else { 32 | 33 | // download file 34 | callback.Dispose(); 35 | 36 | // open the downloads tab 37 | myForm.OpenDownloadsTab(); 38 | 39 | } 40 | 41 | } 42 | } 43 | } 44 | 45 | public void OnDownloadUpdated(IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback) 46 | { 47 | myForm.UpdateDownloadItem(downloadItem); 48 | if (downloadItem.IsInProgress && myForm.CancelRequests.Contains(downloadItem.Id)) { 49 | callback.Cancel(); 50 | } 51 | //Console.WriteLine(downloadItem.Url + " %" + downloadItem.PercentComplete + " complete"); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Handlers/HostHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Forms; 6 | 7 | namespace SharpBrowser { 8 | 9 | /// 10 | /// functions in this class are accessible by JS using the code `host.X()` 11 | /// 12 | internal class HostHandler { 13 | MainForm myForm; 14 | 15 | public HostHandler(MainForm form) { 16 | myForm = form; 17 | } 18 | public void addNewBrowserTab(string url, bool focusNewTab = true) { 19 | myForm.AddNewBrowserTab(url, focusNewTab); 20 | } 21 | public string getDownloads() { 22 | lock (myForm.downloads) { 23 | string x = JSON.Instance.ToJSON(myForm.downloads); 24 | //MessageBox.Show(x); 25 | return x; 26 | } 27 | } 28 | 29 | public bool cancelDownload(int downloadId) { 30 | lock (myForm.downloadCancelRequests) { 31 | if (!myForm.downloadCancelRequests.Contains(downloadId)) { 32 | myForm.downloadCancelRequests.Add(downloadId); 33 | } 34 | } 35 | return true; 36 | } 37 | public void refreshActiveTab() { 38 | myForm.RefreshActiveTab(); 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/Handlers/KeyboardHandler.cs: -------------------------------------------------------------------------------- 1 |  using System; 2 | using System.Collections.Generic; 3 | using System.Windows.Forms; 4 | using CefSharp; 5 | 6 | 7 | namespace SharpBrowser { 8 | internal class KeyboardHandler : IKeyboardHandler 9 | { 10 | MainForm myForm; 11 | 12 | public static List Hotkeys = new List(); 13 | public static void AddHotKey(Form form, Action function, Keys key, bool ctrl = false, bool shift = false, bool alt = false) { 14 | Utils.AddHotKey(form, function, key, ctrl, shift, alt); 15 | Hotkeys.Add(new SharpHotKey(function, key, ctrl, shift, alt)); 16 | } 17 | 18 | public KeyboardHandler(MainForm form) 19 | { 20 | myForm = form; 21 | } 22 | public bool OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut) 23 | { 24 | return false; 25 | } 26 | 27 | /// 28 | public bool OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey) { 29 | 30 | if (type == KeyType.RawKeyDown) { 31 | 32 | 33 | // check if my hotkey 34 | int mod = ((int)modifiers); 35 | bool ctrlDown = mod.IsBitmaskOn((int)CefEventFlags.ControlDown); 36 | bool shiftDown = mod.IsBitmaskOn((int)CefEventFlags.ShiftDown); 37 | bool altDown = mod.IsBitmaskOn((int)CefEventFlags.AltDown); 38 | 39 | // per registered hotkey 40 | foreach (SharpHotKey key in Hotkeys) { 41 | if (key.KeyCode == windowsKeyCode){ 42 | if (key.Ctrl == ctrlDown && key.Shift == shiftDown && key.Alt == altDown) { 43 | myForm.InvokeOnParent(delegate() { 44 | key.Callback(); 45 | }); 46 | } 47 | } 48 | } 49 | 50 | //Debug.WriteLine(String.Format("OnKeyEvent: KeyType: {0} 0x{1:X} Modifiers: {2}", type, windowsKeyCode, modifiers)); 51 | 52 | } 53 | 54 | return false; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Handlers/LifeSpanHandler.cs: -------------------------------------------------------------------------------- 1 | using CefSharp; 2 | 3 | namespace SharpBrowser { 4 | internal class LifeSpanHandler : ILifeSpanHandler 5 | { 6 | MainForm myForm; 7 | 8 | public LifeSpanHandler(MainForm form) 9 | { 10 | myForm = form; 11 | } 12 | 13 | 14 | // Summary: 15 | // Called when a browser has recieved a request to close. This may result directly 16 | // from a call to CefBrowserHost::CloseBrowser() or indirectly if the browser 17 | // is a top-level OS window created by CEF and the user attempts to close the 18 | // window. This method will be called after the JavaScript 'onunload' event 19 | // has been fired. It will not be called for browsers after the associated OS 20 | // window has been destroyed (for those browsers it is no longer possible to 21 | // cancel the close). If CEF created an OS window for the browser returning 22 | // false will send an OS close notification to the browser window's top-level 23 | // owner (e.g. WM_CLOSE on Windows, performClose: on OS-X and "delete_event" 24 | // on Linux). If no OS window exists (window rendering disabled) returning false 25 | // will cause the browser object to be destroyed immediately. Return true if 26 | // the browser is parented to another window and that other window needs to 27 | // receive close notification via some non-standard technique. If an application 28 | // provides its own top-level window it should handle OS close notifications 29 | // by calling CefBrowserHost::CloseBrowser(false) instead of immediately closing 30 | // (see the example below). This gives CEF an opportunity to process the 'onbeforeunload' 31 | // event and optionally cancel the close before DoClose() is called. The CefLifeSpanHandler::OnBeforeClose() 32 | // method will be called immediately before the browser object is destroyed. 33 | // The application should only exit after OnBeforeClose() has been called for 34 | // all existing browsers. If the browser represents a modal window and a custom 35 | // modal loop implementation was provided in CefLifeSpanHandler::RunModal() 36 | // this callback should be used to restore the opener window to a usable state. 37 | // By way of example consider what should happen during window close when the 38 | // browser is parented to an application-provided top-level OS window. 1. User 39 | // clicks the window close button which sends an OS close notification (e.g. 40 | // WM_CLOSE on Windows, performClose: on OS-X and "delete_event" on Linux). 41 | // 2. Application's top-level window receives the close notification and: A. 42 | // Calls CefBrowserHost::CloseBrowser(false). B. Cancels the window close. 43 | // 3. JavaScript 'onbeforeunload' handler executes and shows the close confirmation 44 | // dialog (which can be overridden via CefJSDialogHandler::OnBeforeUnloadDialog()). 45 | // 4. User approves the close. 5. JavaScript 'onunload' handler executes. 46 | // 6. Application's DoClose() handler is called. Application will: A. Set a 47 | // flag to indicate that the next close attempt will be allowed. B. Return 48 | // false. 7. CEF sends an OS close notification. 8. Application's top-level 49 | // window receives the OS close notification and allows the window to close 50 | // based on the flag from #6B. 9. Browser OS window is destroyed. 10. Application's 51 | // CefLifeSpanHandler::OnBeforeClose() handler is called and the browser object 52 | // is destroyed. 11. Application exits by calling CefQuitMessageLoop() if no 53 | // other browsers exist. 54 | // 55 | // Parameters: 56 | // browserControl: 57 | // The CefSharp.IWebBrowser control that is realted to the window is closing. 58 | // 59 | // browser: 60 | // The browser instance 61 | // 62 | // Returns: 63 | // For default behaviour return false 64 | public bool DoClose(IWebBrowser browserControl, IBrowser browser) { 65 | return false; 66 | } 67 | // 68 | // Summary: 69 | // Called after a new browser is created. 70 | // 71 | // Parameters: 72 | // browserControl: 73 | // The CefSharp.IWebBrowser control that is realted to the window is closing. 74 | // 75 | // browser: 76 | // The browser instance 77 | public void OnAfterCreated(IWebBrowser browserControl, IBrowser browser) { 78 | } 79 | // 80 | // Summary: 81 | // Called before a CefBrowser window (either the main browser for CefSharp.IWebBrowser, 82 | // or one of its children) 83 | // 84 | // Parameters: 85 | // browserControl: 86 | // The CefSharp.IWebBrowser control that is realted to the window is closing. 87 | // 88 | // browser: 89 | // The browser instance 90 | public void OnBeforeClose(IWebBrowser browserControl, IBrowser browser) { 91 | } 92 | // 93 | // Summary: 94 | // Called before a popup window is created. 95 | // 96 | // Parameters: 97 | // browserControl: 98 | // The CefSharp.IWebBrowser control this request is for. 99 | // 100 | // browser: 101 | // The browser instance that launched this popup. 102 | // 103 | // frame: 104 | // The HTML frame that launched this popup. 105 | // 106 | // targetUrl: 107 | // The URL of the popup content. (This may be empty/null) 108 | // 109 | // targetFrameName: 110 | // The name of the popup. (This may be empty/null) 111 | // 112 | // targetDisposition: 113 | // The value indicates where the user intended to open the popup (e.g. current 114 | // tab, new tab, etc) 115 | // 116 | // userGesture: 117 | // The value will be true if the popup was opened via explicit user gesture 118 | // (e.g. clicking a link) or false if the popup opened automatically (e.g. via 119 | // the DomContentLoaded event). 120 | // 121 | // popupFeatures: 122 | // structure contains additional information about the requested popup window 123 | // 124 | // windowInfo: 125 | // window information 126 | // 127 | // browserSettings: 128 | // browser settings, defaults to source browsers 129 | // 130 | // noJavascriptAccess: 131 | // value indicates whether the new browser window should be scriptable and in 132 | // the same process as the source browser. 133 | // 134 | // newBrowser: 135 | // EXPERIMENTAL - A newly created browser that will host the popup 136 | // 137 | // Returns: 138 | // To cancel creation of the popup window return true otherwise return false. 139 | // 140 | // Remarks: 141 | // CEF documentation: Called on the IO thread before a new popup window is created. 142 | // The |browser| and |frame| parameters represent the source of the popup request. 143 | // The |target_url| and |target_frame_name| values may be empty if none were 144 | // specified with the request. The |popupFeatures| structure contains information 145 | // about the requested popup window. To allow creation of the popup window optionally 146 | // modify |windowInfo|, |client|, |settings| and |no_javascript_access| and 147 | // return false. To cancel creation of the popup window return true. The |client| 148 | // and |settings| values will default to the source browser's values. The |no_javascript_access| 149 | // value indicates whether the new browser window should be scriptable and in 150 | // the same process as the source browser. 151 | 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) { 152 | 153 | // open popup in new tab! 154 | newBrowser = myForm.AddNewBrowserTab(targetUrl); 155 | 156 | return true; 157 | 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/Handlers/RequestHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | using CefSharp; 3 | 4 | namespace SharpBrowser { 5 | internal class RequestHandler : IRequestHandler { 6 | MainForm myForm; 7 | 8 | public RequestHandler(MainForm form) { 9 | myForm = form; 10 | } 11 | 12 | // Summary: 13 | // Called when the browser needs credentials from the user. 14 | // 15 | // Parameters: 16 | // frame: 17 | // The frame object that needs credentials (This will contain the URL that is 18 | // being requested.) 19 | // 20 | // isProxy: 21 | // indicates whether the host is a proxy server 22 | // 23 | // callback: 24 | // Callback interface used for asynchronous continuation of authentication requests. 25 | // 26 | // Returns: 27 | // Return true to continue the request and call CefAuthCallback::Continue() 28 | // when the authentication information is available. Return false to cancel 29 | // the request. 30 | public bool GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback) { 31 | 32 | return false; 33 | } 34 | // 35 | // Summary: 36 | // Called on the CEF IO thread to optionally filter resource response content. 37 | // 38 | // Parameters: 39 | // frame: 40 | // The frame that is being redirected. 41 | // 42 | // request: 43 | // the request object - cannot be modified in this callback 44 | // 45 | // response: 46 | // the response object - cannot be modified in this callback 47 | // 48 | // Returns: 49 | // Return an IResponseFilter to intercept this response, otherwise return null 50 | public IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response) { 51 | return null; 52 | } 53 | // 54 | // Summary: 55 | // Called before browser navigation. If the navigation is allowed CefSharp.IWebBrowser.FrameLoadStart 56 | // and CefSharp.IWebBrowser.FrameLoadEnd will be called. If the navigation is 57 | // canceled CefSharp.IWebBrowser.LoadError will be called with an ErrorCode 58 | // value of CefSharp.CefErrorCode.Aborted. 59 | // 60 | // Parameters: 61 | // frame: 62 | // The frame the request is coming from 63 | // 64 | // request: 65 | // the request object - cannot be modified in this callback 66 | // 67 | // isRedirect: 68 | // has the request been redirected 69 | // 70 | // Returns: 71 | // Return true to cancel the navigation or false to allow the navigation to 72 | // proceed. 73 | public bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect) { 74 | return false; 75 | } 76 | // 77 | // Summary: 78 | // Called before a resource request is loaded. For async processing return CefSharp.CefReturnValue.ContinueAsync 79 | // and execute CefSharp.IRequestCallback.Continue(System.Boolean) or CefSharp.IRequestCallback.Cancel() 80 | // 81 | // Parameters: 82 | // frame: 83 | // The frame object 84 | // 85 | // request: 86 | // the request object - can be modified in this callback. 87 | // 88 | // callback: 89 | // Callback interface used for asynchronous continuation of url requests. 90 | // 91 | // Returns: 92 | // To cancel loading of the resource return CefSharp.CefReturnValue.Cancel or 93 | // CefSharp.CefReturnValue.Continue to allow the resource to load normally. 94 | // For async return CefSharp.CefReturnValue.ContinueAsync 95 | public CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback) { 96 | 97 | // if referer given 98 | var tab = myForm.GetTabByBrowser(browserControl); 99 | if (tab != null && tab.RefererURL != null) { 100 | 101 | // Set referer 102 | request.SetReferrer(tab.RefererURL, ReferrerPolicy.Always); 103 | 104 | } 105 | 106 | return CefSharp.CefReturnValue.Continue; 107 | } 108 | // 109 | // Summary: 110 | // Called to handle requests for URLs with an invalid SSL certificate. Return 111 | // true and call CefSharp.IRequestCallback.Continue(System.Boolean) either in 112 | // this method or at a later time to continue or cancel the request. If CefSettings.IgnoreCertificateErrors 113 | // is set all invalid certificates will be accepted without calling this method. 114 | // 115 | // Parameters: 116 | // errorCode: 117 | // the error code for this invalid certificate 118 | // 119 | // requestUrl: 120 | // the url of the request for the invalid certificate 121 | // 122 | // sslInfo: 123 | // ssl certificate information 124 | // 125 | // callback: 126 | // Callback interface used for asynchronous continuation of url requests. If 127 | // empty the error cannot be recovered from and the request will be canceled 128 | // automatically. 129 | // 130 | // Returns: 131 | // Return false to cancel the request immediately. Return true and use CefSharp.IRequestCallback 132 | // to execute in an async fashion. 133 | public bool OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback) { 134 | return true; 135 | } 136 | // 137 | // Summary: 138 | // Called on the UI thread before OnBeforeBrowse in certain limited cases where 139 | // navigating a new or different browser might be desirable. This includes user-initiated 140 | // navigation that might open in a special way (e.g. links clicked via middle-click 141 | // or ctrl + left-click) and certain types of cross-origin navigation initiated 142 | // from the renderer process (e.g. navigating the top-level frame to/from a 143 | // file URL). 144 | // 145 | // Parameters: 146 | // frame: 147 | // The frame object 148 | // 149 | // targetDisposition: 150 | // The value indicates where the user intended to navigate the browser based 151 | // on standard Chromium behaviors (e.g. current tab, new tab, etc). 152 | // 153 | // userGesture: 154 | // The value will be true if the browser navigated via explicit user gesture 155 | // (e.g. clicking a link) or false if it navigated automatically (e.g. via the 156 | // DomContentLoaded event). 157 | // 158 | // Returns: 159 | // Return true to cancel the navigation or false to allow the navigation to 160 | // proceed in the source browser's top-level frame. 161 | public bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture) { 162 | return false; 163 | } 164 | // 165 | // Summary: 166 | // Called when a plugin has crashed 167 | // 168 | // Parameters: 169 | // pluginPath: 170 | // path of the plugin that crashed 171 | public void OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath) { 172 | } 173 | // 174 | // Summary: 175 | // Called on the UI thread to handle requests for URLs with an unknown protocol 176 | // component. SECURITY WARNING: YOU SHOULD USE THIS METHOD TO ENFORCE RESTRICTIONS 177 | // BASED ON SCHEME, HOST OR OTHER URL ANALYSIS BEFORE ALLOWING OS EXECUTION. 178 | // 179 | // Parameters: 180 | // url: 181 | // the request url 182 | // 183 | // Returns: 184 | // return to true to attempt execution via the registered OS protocol handler, 185 | // if any. Otherwise return false. 186 | public bool OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url) { 187 | return true; 188 | } 189 | // 190 | // Summary: 191 | // Called when JavaScript requests a specific storage quota size via the webkitStorageInfo.requestQuota 192 | // function. For async processing return true and execute CefSharp.IRequestCallback.Continue(System.Boolean) 193 | // at a later time to grant or deny the request or CefSharp.IRequestCallback.Cancel() 194 | // to cancel. 195 | // 196 | // Parameters: 197 | // originUrl: 198 | // the origin of the page making the request 199 | // 200 | // newSize: 201 | // is the requested quota size in bytes 202 | // 203 | // callback: 204 | // Callback interface used for asynchronous continuation of url requests. 205 | // 206 | // Returns: 207 | // Return false to cancel the request immediately. Return true to continue the 208 | // request and call CefSharp.IRequestCallback.Continue(System.Boolean) either 209 | // in this method or at a later time to grant or deny the request. 210 | public bool OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl, long newSize, IRequestCallback callback) { 211 | callback.Continue(true); 212 | return true; 213 | } 214 | // 215 | // Summary: 216 | // Called when the render process terminates unexpectedly. 217 | // 218 | // Parameters: 219 | // status: 220 | // indicates how the process terminated. 221 | public void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status) { 222 | } 223 | // 224 | // Summary: 225 | // Called on the CEF UI thread when the render view associated with browser 226 | // is ready to receive/handle IPC messages in the render process. 227 | public void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser) { 228 | } 229 | // 230 | // Summary: 231 | // Called on the CEF IO thread when a resource load has completed. 232 | // 233 | // Parameters: 234 | // frame: 235 | // The frame that is being redirected. 236 | // 237 | // request: 238 | // the request object - cannot be modified in this callback 239 | // 240 | // response: 241 | // the response object - cannot be modified in this callback 242 | // 243 | // status: 244 | // indicates the load completion status 245 | // 246 | // receivedContentLength: 247 | // is the number of response bytes actually read. 248 | public void OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength) { 249 | } 250 | // 251 | // Summary: 252 | // Called on the IO thread when a resource load is redirected. The CefSharp.IRequest.Url 253 | // parameter will contain the old URL and other request-related information. 254 | // 255 | // Parameters: 256 | // frame: 257 | // The frame that is being redirected. 258 | // 259 | // request: 260 | // the request object - cannot be modified in this callback 261 | // 262 | // newUrl: 263 | // the new URL and can be changed if desired 264 | public void OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, ref string newUrl) { 265 | } 266 | // 267 | // Summary: 268 | // Called on the CEF IO thread when a resource response is received. To allow 269 | // the resource to load normally return false. To redirect or retry the resource 270 | // modify request (url, headers or post body) and return true. The response 271 | // object cannot be modified in this callback. 272 | // 273 | // Parameters: 274 | // frame: 275 | // The frame that is being redirected. 276 | // 277 | // request: 278 | // the request object 279 | // 280 | // response: 281 | // the response object - cannot be modified in this callback 282 | // 283 | // Returns: 284 | // To allow the resource to load normally return false. To redirect or retry 285 | // the resource modify request (url, headers or post body) and return true. 286 | public bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response) { 287 | 288 | 289 | int code = response.StatusCode; 290 | 291 | 292 | // if NOT FOUND 293 | if (code == 404) { 294 | 295 | if (!request.Url.IsURLLocalhost()) { 296 | 297 | // redirect to web archive to try and find older version 298 | request.Url = "http://web.archive.org/web/*/" + request.Url; 299 | 300 | } else { 301 | 302 | // show offline "file not found" page 303 | request.Url = MainForm.FileNotFoundURL + "?path=" + request.Url.EncodeURL(); 304 | } 305 | 306 | return true; 307 | } 308 | 309 | 310 | // if FILE NOT FOUND 311 | if (code == 0 && request.Url.IsURLOfflineFile()) { 312 | string path = request.Url.FileURLToPath(); 313 | if (path.FileNotExists()) { 314 | 315 | // show offline "file not found" page 316 | request.Url = MainForm.FileNotFoundURL + "?path=" + path.EncodeURL(); 317 | return true; 318 | 319 | } 320 | } else { 321 | 322 | // if CANNOT CONNECT 323 | if (code == 0 || code == 444 || (code >= 500 && code <= 599)) { 324 | 325 | // show offline "cannot connect to server" page 326 | request.Url = MainForm.CannotConnectURL; 327 | return true; 328 | } 329 | 330 | } 331 | 332 | return false; 333 | } 334 | 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /src/Handlers/SchemeHandler.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Net; 6 | using System.Threading.Tasks; 7 | using CefSharp; 8 | using System.Windows.Forms; 9 | using System.Drawing; 10 | 11 | namespace SharpBrowser { 12 | internal class SchemeHandler : IResourceHandler, IDisposable 13 | { 14 | private static string appPath = Path.GetDirectoryName(Application.ExecutablePath) + @"\"; 15 | 16 | private string mimeType; 17 | private Stream stream; 18 | MainForm myForm; 19 | private Uri uri; 20 | private string fileName; 21 | 22 | public SchemeHandler(MainForm form) { 23 | myForm = form; 24 | } 25 | 26 | public void Dispose() { 27 | 28 | } 29 | 30 | 31 | // 32 | // Summary: 33 | // Begin processing the request. 34 | // 35 | // Parameters: 36 | // request: 37 | // The request object. 38 | // 39 | // callback: 40 | // The callback used to Continue or Cancel the request (async). 41 | // 42 | // Returns: 43 | // To handle the request return true and call CefSharp.ICallback.Continue() 44 | // once the response header information is available CefSharp.ICallback.Continue() 45 | // can also be called from inside this method if header information is available 46 | // immediately). To cancel the request return false. 47 | public bool ProcessRequest(IRequest request, ICallback callback) { 48 | uri = new Uri(request.Url); 49 | fileName = uri.AbsolutePath; 50 | 51 | // if url is blocked 52 | /*if (!myForm.IsURLOk(request.Url)) { 53 | 54 | // return true so it does not open up 55 | return true; 56 | }*/ 57 | 58 | // if url is browser file 59 | if (uri.Host == "storage") { 60 | fileName = appPath + uri.Host + fileName; 61 | if (File.Exists(fileName)) { 62 | Task.Factory.StartNew(() => { 63 | using (callback) { 64 | //var bytes = Encoding.UTF8.GetBytes(resource); 65 | //stream = new MemoryStream(bytes); 66 | FileStream fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); 67 | mimeType = ResourceHandler.GetMimeType(Path.GetExtension(fileName)); 68 | stream = fStream; 69 | callback.Continue(); 70 | } 71 | }); 72 | 73 | return true; 74 | } 75 | } 76 | 77 | // if url is request for icon of another file 78 | if (uri.Host == "fileicon") { 79 | Task.Factory.StartNew(() => { 80 | using (callback) { 81 | stream = FileIconUtils.GetFileIcon(fileName, FileIconSize.Large); 82 | mimeType = ResourceHandler.GetMimeType(".png"); 83 | callback.Continue(); 84 | } 85 | }); 86 | return true; 87 | } 88 | 89 | 90 | // by default reject 91 | callback.Dispose(); 92 | return false; 93 | } 94 | // 95 | // Summary: 96 | // Retrieve response header information. If the response length is not known 97 | // set responseLength to -1 and ReadResponse() will be called until it returns 98 | // false. If the response length is known set responseLength to a positive value 99 | // and ReadResponse() will be called until it returns false or the specified 100 | // number of bytes have been read. If an error occured while setting up the 101 | // request you can set CefSharp.IResponse.ErrorCode to indicate the error condition. 102 | // 103 | // Parameters: 104 | // response: 105 | // Use the response object to set the mime type, http status code and other 106 | // optional header values. 107 | // 108 | // responseLength: 109 | // If the response length is not known set responseLength to -1 110 | // 111 | // redirectUrl: 112 | // To redirect the request to a new URL set redirectUrl to the new Url. 113 | public void GetResponseHeaders(IResponse response, out long responseLength, out string redirectUrl) { 114 | 115 | responseLength = stream.Length; 116 | redirectUrl = null; 117 | 118 | response.StatusCode = (int)HttpStatusCode.OK; 119 | response.StatusText = "OK"; 120 | response.MimeType = mimeType; 121 | 122 | //return stream; 123 | } 124 | // 125 | // Summary: 126 | // Read response data. If data is available immediately copy to dataOut, set 127 | // bytesRead to the number of bytes copied, and return true. To read the data 128 | // at a later time set bytesRead to 0, return true and call ICallback.Continue() 129 | // when the data is available. To indicate response completion return false. 130 | // 131 | // Parameters: 132 | // dataOut: 133 | // Stream to write to 134 | // 135 | // bytesRead: 136 | // Number of bytes copied to the stream 137 | // 138 | // callback: 139 | // The callback used to Continue or Cancel the request (async). 140 | // 141 | // Returns: 142 | // If data is available immediately copy to dataOut, set bytesRead to the number 143 | // of bytes copied, and return true.To indicate response completion return false. 144 | // 145 | // Remarks: 146 | // Depending on this size of your response this method may be called multiple 147 | // times 148 | public bool ReadResponse(Stream dataOut, out int bytesRead, ICallback callback) { 149 | 150 | //Dispose the callback as it's an unmanaged resource, we don't need it in this case 151 | callback.Dispose(); 152 | 153 | if (stream == null) { 154 | bytesRead = 0; 155 | return false; 156 | } 157 | 158 | //Data out represents an underlying buffer (typically 32kb in size). 159 | var buffer = new byte[dataOut.Length]; 160 | bytesRead = stream.Read(buffer, 0, buffer.Length); 161 | 162 | dataOut.Write(buffer, 0, buffer.Length); 163 | 164 | return bytesRead > 0; 165 | 166 | } 167 | // Summary: 168 | // Request processing has been canceled. 169 | public void Cancel() { 170 | } 171 | // 172 | // Summary: 173 | // Return true if the specified cookie can be sent with the request or false 174 | // otherwise. If false is returned for any cookie then no cookies will be sent 175 | // with the request. 176 | public bool CanGetCookie(CefSharp.Cookie cookie) { 177 | return true; 178 | } 179 | // 180 | // Summary: 181 | // Return true if the specified cookie returned with the response can be set 182 | // or false otherwise. 183 | public bool CanSetCookie(CefSharp.Cookie cookie) { 184 | return true; 185 | } 186 | 187 | } 188 | } -------------------------------------------------------------------------------- /src/Handlers/SchemeHandlerFactory.cs: -------------------------------------------------------------------------------- 1 | using CefSharp; 2 | 3 | namespace SharpBrowser { 4 | internal class SchemeHandlerFactory : ISchemeHandlerFactory 5 | { 6 | 7 | public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request) 8 | { 9 | return new SchemeHandler(MainForm.Instance); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/MainForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace SharpBrowser 2 | { 3 | partial class MainForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.components = new System.ComponentModel.Container(); 32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); 33 | this.menuStripTab = new System.Windows.Forms.ContextMenuStrip(this.components); 34 | this.menuCloseTab = new System.Windows.Forms.ToolStripMenuItem(); 35 | this.menuCloseOtherTabs = new System.Windows.Forms.ToolStripMenuItem(); 36 | this.BtnRefresh = new System.Windows.Forms.Button(); 37 | this.BtnStop = new System.Windows.Forms.Button(); 38 | this.BtnForward = new System.Windows.Forms.Button(); 39 | this.BtnBack = new System.Windows.Forms.Button(); 40 | this.timer1 = new System.Windows.Forms.Timer(this.components); 41 | this.BtnDownloads = new System.Windows.Forms.Button(); 42 | this.TxtURL = new System.Windows.Forms.TextBox(); 43 | this.PanelToolbar = new System.Windows.Forms.Panel(); 44 | this.TabPages = new FarsiLibrary.Win.FATabStrip(); 45 | this.tabStrip1 = new FarsiLibrary.Win.FATabStripItem(); 46 | this.tabStripAdd = new FarsiLibrary.Win.FATabStripItem(); 47 | this.PanelStatus = new System.Windows.Forms.Panel(); 48 | this.PanelSearch = new System.Windows.Forms.Panel(); 49 | this.BtnNextSearch = new System.Windows.Forms.Button(); 50 | this.BtnPrevSearch = new System.Windows.Forms.Button(); 51 | this.BtnCloseSearch = new System.Windows.Forms.Button(); 52 | this.TxtSearch = new System.Windows.Forms.TextBox(); 53 | this.menuStripTab.SuspendLayout(); 54 | this.PanelToolbar.SuspendLayout(); 55 | ((System.ComponentModel.ISupportInitialize)(this.TabPages)).BeginInit(); 56 | this.TabPages.SuspendLayout(); 57 | this.PanelSearch.SuspendLayout(); 58 | this.SuspendLayout(); 59 | // 60 | // menuStripTab 61 | // 62 | this.menuStripTab.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 63 | this.menuCloseTab, 64 | this.menuCloseOtherTabs}); 65 | this.menuStripTab.Name = "menuStripTab"; 66 | this.menuStripTab.Size = new System.Drawing.Size(198, 52); 67 | // 68 | // menuCloseTab 69 | // 70 | this.menuCloseTab.Name = "menuCloseTab"; 71 | this.menuCloseTab.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.F4))); 72 | this.menuCloseTab.Size = new System.Drawing.Size(197, 24); 73 | this.menuCloseTab.Text = "Close tab"; 74 | this.menuCloseTab.Click += new System.EventHandler(this.menuCloseTab_Click); 75 | // 76 | // menuCloseOtherTabs 77 | // 78 | this.menuCloseOtherTabs.Name = "menuCloseOtherTabs"; 79 | this.menuCloseOtherTabs.Size = new System.Drawing.Size(197, 24); 80 | this.menuCloseOtherTabs.Text = "Close other tabs"; 81 | this.menuCloseOtherTabs.Click += new System.EventHandler(this.menuCloseOtherTabs_Click); 82 | // 83 | // BtnRefresh 84 | // 85 | this.BtnRefresh.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 86 | this.BtnRefresh.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 87 | this.BtnRefresh.ForeColor = System.Drawing.Color.White; 88 | this.BtnRefresh.Image = ((System.Drawing.Image)(resources.GetObject("BtnRefresh.Image"))); 89 | this.BtnRefresh.Location = new System.Drawing.Point(878, 0); 90 | this.BtnRefresh.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 91 | this.BtnRefresh.Name = "BtnRefresh"; 92 | this.BtnRefresh.Size = new System.Drawing.Size(25, 30); 93 | this.BtnRefresh.TabIndex = 3; 94 | this.BtnRefresh.UseVisualStyleBackColor = true; 95 | this.BtnRefresh.Click += new System.EventHandler(this.bRefresh_Click); 96 | // 97 | // BtnStop 98 | // 99 | this.BtnStop.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 100 | this.BtnStop.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 101 | this.BtnStop.ForeColor = System.Drawing.Color.White; 102 | this.BtnStop.Image = ((System.Drawing.Image)(resources.GetObject("BtnStop.Image"))); 103 | this.BtnStop.Location = new System.Drawing.Point(878, -2); 104 | this.BtnStop.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 105 | this.BtnStop.Name = "BtnStop"; 106 | this.BtnStop.Size = new System.Drawing.Size(25, 30); 107 | this.BtnStop.TabIndex = 2; 108 | this.BtnStop.UseVisualStyleBackColor = true; 109 | this.BtnStop.Click += new System.EventHandler(this.bStop_Click); 110 | // 111 | // BtnForward 112 | // 113 | this.BtnForward.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 114 | this.BtnForward.ForeColor = System.Drawing.Color.White; 115 | this.BtnForward.Image = ((System.Drawing.Image)(resources.GetObject("BtnForward.Image"))); 116 | this.BtnForward.Location = new System.Drawing.Point(29, 0); 117 | this.BtnForward.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 118 | this.BtnForward.Name = "BtnForward"; 119 | this.BtnForward.Size = new System.Drawing.Size(25, 30); 120 | this.BtnForward.TabIndex = 1; 121 | this.BtnForward.UseVisualStyleBackColor = true; 122 | this.BtnForward.Click += new System.EventHandler(this.bForward_Click); 123 | // 124 | // BtnBack 125 | // 126 | this.BtnBack.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 127 | this.BtnBack.ForeColor = System.Drawing.Color.White; 128 | this.BtnBack.Image = ((System.Drawing.Image)(resources.GetObject("BtnBack.Image"))); 129 | this.BtnBack.Location = new System.Drawing.Point(2, 0); 130 | this.BtnBack.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 131 | this.BtnBack.Name = "BtnBack"; 132 | this.BtnBack.Size = new System.Drawing.Size(25, 30); 133 | this.BtnBack.TabIndex = 0; 134 | this.BtnBack.UseVisualStyleBackColor = true; 135 | this.BtnBack.Click += new System.EventHandler(this.bBack_Click); 136 | // 137 | // timer1 138 | // 139 | this.timer1.Interval = 50; 140 | this.timer1.Tick += new System.EventHandler(this.timer1_Tick); 141 | // 142 | // BtnDownloads 143 | // 144 | this.BtnDownloads.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 145 | this.BtnDownloads.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 146 | this.BtnDownloads.ForeColor = System.Drawing.Color.White; 147 | this.BtnDownloads.Image = ((System.Drawing.Image)(resources.GetObject("BtnDownloads.Image"))); 148 | this.BtnDownloads.Location = new System.Drawing.Point(906, 0); 149 | this.BtnDownloads.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 150 | this.BtnDownloads.Name = "BtnDownloads"; 151 | this.BtnDownloads.Size = new System.Drawing.Size(25, 30); 152 | this.BtnDownloads.TabIndex = 4; 153 | this.BtnDownloads.Tag = "Downloads"; 154 | this.BtnDownloads.UseVisualStyleBackColor = true; 155 | this.BtnDownloads.Click += new System.EventHandler(this.bDownloads_Click); 156 | // 157 | // TxtURL 158 | // 159 | this.TxtURL.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 160 | | System.Windows.Forms.AnchorStyles.Right))); 161 | this.TxtURL.BorderStyle = System.Windows.Forms.BorderStyle.None; 162 | this.TxtURL.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 163 | this.TxtURL.Location = new System.Drawing.Point(60, 5); 164 | this.TxtURL.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 165 | this.TxtURL.Name = "TxtURL"; 166 | this.TxtURL.Size = new System.Drawing.Size(812, 27); 167 | this.TxtURL.TabIndex = 5; 168 | this.TxtURL.Click += new System.EventHandler(this.txtUrl_Click); 169 | this.TxtURL.TextChanged += new System.EventHandler(this.txtUrl_TextChanged); 170 | this.TxtURL.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TxtURL_KeyDown); 171 | // 172 | // PanelToolbar 173 | // 174 | this.PanelToolbar.BackColor = System.Drawing.Color.White; 175 | this.PanelToolbar.Controls.Add(this.TxtURL); 176 | this.PanelToolbar.Controls.Add(this.BtnDownloads); 177 | this.PanelToolbar.Controls.Add(this.BtnForward); 178 | this.PanelToolbar.Controls.Add(this.BtnBack); 179 | this.PanelToolbar.Controls.Add(this.BtnRefresh); 180 | this.PanelToolbar.Controls.Add(this.BtnStop); 181 | this.PanelToolbar.Dock = System.Windows.Forms.DockStyle.Top; 182 | this.PanelToolbar.Location = new System.Drawing.Point(0, 0); 183 | this.PanelToolbar.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 184 | this.PanelToolbar.Name = "PanelToolbar"; 185 | this.PanelToolbar.Size = new System.Drawing.Size(934, 30); 186 | this.PanelToolbar.TabIndex = 6; 187 | // 188 | // TabPages 189 | // 190 | this.TabPages.ContextMenuStrip = this.menuStripTab; 191 | this.TabPages.Dock = System.Windows.Forms.DockStyle.Fill; 192 | this.TabPages.Font = new System.Drawing.Font("Segoe UI", 10.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 193 | this.TabPages.Items.AddRange(new FarsiLibrary.Win.FATabStripItem[] { 194 | this.tabStrip1, 195 | this.tabStripAdd}); 196 | this.TabPages.Location = new System.Drawing.Point(0, 30); 197 | this.TabPages.Name = "TabPages"; 198 | this.TabPages.SelectedItem = this.tabStrip1; 199 | this.TabPages.Size = new System.Drawing.Size(934, 621); 200 | this.TabPages.TabIndex = 4; 201 | this.TabPages.Text = "faTabStrip1"; 202 | this.TabPages.TabStripItemSelectionChanged += new FarsiLibrary.Win.TabStripItemChangedHandler(this.OnTabsChanged); 203 | this.TabPages.TabStripItemClosed += new System.EventHandler(this.OnTabClosed); 204 | this.TabPages.MouseClick += new System.Windows.Forms.MouseEventHandler(this.tabPages_MouseClick); 205 | // 206 | // tabStrip1 207 | // 208 | this.tabStrip1.IsDrawn = true; 209 | this.tabStrip1.Name = "tabStrip1"; 210 | this.tabStrip1.Selected = true; 211 | this.tabStrip1.Size = new System.Drawing.Size(932, 591); 212 | this.tabStrip1.TabIndex = 0; 213 | this.tabStrip1.Title = "Loading..."; 214 | // 215 | // tabStripAdd 216 | // 217 | this.tabStripAdd.CanClose = false; 218 | this.tabStripAdd.IsDrawn = true; 219 | this.tabStripAdd.Name = "tabStripAdd"; 220 | this.tabStripAdd.Size = new System.Drawing.Size(931, 601); 221 | this.tabStripAdd.TabIndex = 1; 222 | this.tabStripAdd.Title = "+"; 223 | // 224 | // PanelStatus 225 | // 226 | this.PanelStatus.Dock = System.Windows.Forms.DockStyle.Bottom; 227 | this.PanelStatus.Location = new System.Drawing.Point(0, 651); 228 | this.PanelStatus.Name = "PanelStatus"; 229 | this.PanelStatus.Size = new System.Drawing.Size(934, 20); 230 | this.PanelStatus.TabIndex = 8; 231 | // 232 | // PanelSearch 233 | // 234 | this.PanelSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 235 | this.PanelSearch.BackColor = System.Drawing.Color.White; 236 | this.PanelSearch.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 237 | this.PanelSearch.Controls.Add(this.BtnNextSearch); 238 | this.PanelSearch.Controls.Add(this.BtnPrevSearch); 239 | this.PanelSearch.Controls.Add(this.BtnCloseSearch); 240 | this.PanelSearch.Controls.Add(this.TxtSearch); 241 | this.PanelSearch.Location = new System.Drawing.Point(625, 41); 242 | this.PanelSearch.Name = "PanelSearch"; 243 | this.PanelSearch.Size = new System.Drawing.Size(307, 40); 244 | this.PanelSearch.TabIndex = 9; 245 | this.PanelSearch.Visible = false; 246 | // 247 | // BtnNextSearch 248 | // 249 | this.BtnNextSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 250 | this.BtnNextSearch.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 251 | this.BtnNextSearch.ForeColor = System.Drawing.Color.White; 252 | this.BtnNextSearch.Image = ((System.Drawing.Image)(resources.GetObject("BtnNextSearch.Image"))); 253 | this.BtnNextSearch.Location = new System.Drawing.Point(239, 4); 254 | this.BtnNextSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 255 | this.BtnNextSearch.Name = "BtnNextSearch"; 256 | this.BtnNextSearch.Size = new System.Drawing.Size(25, 30); 257 | this.BtnNextSearch.TabIndex = 9; 258 | this.BtnNextSearch.Tag = "Find next (Enter)"; 259 | this.BtnNextSearch.UseVisualStyleBackColor = true; 260 | this.BtnNextSearch.Click += new System.EventHandler(this.BtnNextSearch_Click); 261 | // 262 | // BtnPrevSearch 263 | // 264 | this.BtnPrevSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 265 | this.BtnPrevSearch.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 266 | this.BtnPrevSearch.ForeColor = System.Drawing.Color.White; 267 | this.BtnPrevSearch.Image = ((System.Drawing.Image)(resources.GetObject("BtnPrevSearch.Image"))); 268 | this.BtnPrevSearch.Location = new System.Drawing.Point(206, 4); 269 | this.BtnPrevSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 270 | this.BtnPrevSearch.Name = "BtnPrevSearch"; 271 | this.BtnPrevSearch.Size = new System.Drawing.Size(25, 30); 272 | this.BtnPrevSearch.TabIndex = 8; 273 | this.BtnPrevSearch.Tag = "Find previous (Shift+Enter)"; 274 | this.BtnPrevSearch.UseVisualStyleBackColor = true; 275 | this.BtnPrevSearch.Click += new System.EventHandler(this.BtnPrevSearch_Click); 276 | // 277 | // BtnCloseSearch 278 | // 279 | this.BtnCloseSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 280 | this.BtnCloseSearch.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 281 | this.BtnCloseSearch.ForeColor = System.Drawing.Color.White; 282 | this.BtnCloseSearch.Image = ((System.Drawing.Image)(resources.GetObject("BtnCloseSearch.Image"))); 283 | this.BtnCloseSearch.Location = new System.Drawing.Point(272, 4); 284 | this.BtnCloseSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 285 | this.BtnCloseSearch.Name = "BtnCloseSearch"; 286 | this.BtnCloseSearch.Size = new System.Drawing.Size(25, 30); 287 | this.BtnCloseSearch.TabIndex = 7; 288 | this.BtnCloseSearch.Tag = "Close (Esc)"; 289 | this.BtnCloseSearch.UseVisualStyleBackColor = true; 290 | this.BtnCloseSearch.Click += new System.EventHandler(this.BtnClearSearch_Click); 291 | // 292 | // TxtSearch 293 | // 294 | this.TxtSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 295 | | System.Windows.Forms.AnchorStyles.Right))); 296 | this.TxtSearch.BorderStyle = System.Windows.Forms.BorderStyle.None; 297 | this.TxtSearch.Font = new System.Drawing.Font("Segoe UI", 13.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 298 | this.TxtSearch.Location = new System.Drawing.Point(10, 6); 299 | this.TxtSearch.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 300 | this.TxtSearch.Name = "TxtSearch"; 301 | this.TxtSearch.Size = new System.Drawing.Size(181, 31); 302 | this.TxtSearch.TabIndex = 6; 303 | this.TxtSearch.TextChanged += new System.EventHandler(this.TxtSearch_TextChanged); 304 | this.TxtSearch.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TxtSearch_KeyDown); 305 | // 306 | // MainForm 307 | // 308 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; 309 | this.ClientSize = new System.Drawing.Size(934, 671); 310 | this.Controls.Add(this.PanelSearch); 311 | this.Controls.Add(this.TabPages); 312 | this.Controls.Add(this.PanelToolbar); 313 | this.Controls.Add(this.PanelStatus); 314 | this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 315 | this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 316 | this.Name = "MainForm"; 317 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 318 | this.Text = "Title"; 319 | this.WindowState = System.Windows.Forms.FormWindowState.Maximized; 320 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); 321 | this.Load += new System.EventHandler(this.MainForm_Load); 322 | this.menuStripTab.ResumeLayout(false); 323 | this.PanelToolbar.ResumeLayout(false); 324 | this.PanelToolbar.PerformLayout(); 325 | ((System.ComponentModel.ISupportInitialize)(this.TabPages)).EndInit(); 326 | this.TabPages.ResumeLayout(false); 327 | this.PanelSearch.ResumeLayout(false); 328 | this.PanelSearch.PerformLayout(); 329 | this.ResumeLayout(false); 330 | 331 | } 332 | 333 | #endregion 334 | 335 | private FarsiLibrary.Win.FATabStrip TabPages; 336 | private FarsiLibrary.Win.FATabStripItem tabStrip1; 337 | private FarsiLibrary.Win.FATabStripItem tabStripAdd; 338 | private System.Windows.Forms.Timer timer1; 339 | private System.Windows.Forms.ContextMenuStrip menuStripTab; 340 | private System.Windows.Forms.ToolStripMenuItem menuCloseTab; 341 | private System.Windows.Forms.ToolStripMenuItem menuCloseOtherTabs; 342 | private System.Windows.Forms.Button BtnForward; 343 | private System.Windows.Forms.Button BtnBack; 344 | private System.Windows.Forms.Button BtnStop; 345 | private System.Windows.Forms.Button BtnRefresh; 346 | private System.Windows.Forms.Button BtnDownloads; 347 | private System.Windows.Forms.TextBox TxtURL; 348 | private System.Windows.Forms.Panel PanelToolbar; 349 | private System.Windows.Forms.Panel PanelStatus; 350 | private System.Windows.Forms.Panel PanelSearch; 351 | private System.Windows.Forms.TextBox TxtSearch; 352 | private System.Windows.Forms.Button BtnCloseSearch; 353 | private System.Windows.Forms.Button BtnPrevSearch; 354 | private System.Windows.Forms.Button BtnNextSearch; 355 | } 356 | } 357 | 358 | -------------------------------------------------------------------------------- /src/MainForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using System.Threading; 4 | using System.Diagnostics; 5 | using System.Configuration; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Web; 10 | using CefSharp; 11 | using CefSharp.WinForms; 12 | using FarsiLibrary.Win; 13 | using Timer = System.Windows.Forms.Timer; 14 | using System.Drawing; 15 | using System.Reflection; 16 | 17 | namespace SharpBrowser { 18 | 19 | /// 20 | /// The main SharpBrowser form, supporting multiple tabs. 21 | /// We used the x86 version of CefSharp V51, so the app works on 32-bit and 64-bit machines. 22 | /// If you would only like to support 64-bit machines, simply change the DLL references. 23 | /// 24 | internal partial class MainForm : Form { 25 | 26 | private string appPath = Path.GetDirectoryName(Application.ExecutablePath) + @"\"; 27 | 28 | public static MainForm Instance; 29 | 30 | public static string Branding = "SharpBrowser"; 31 | 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"; 32 | public static string HomepageURL = "https://www.google.com"; 33 | public static string NewTabURL = "about:blank"; 34 | public static string DownloadsURL = "sharpbrowser://storage/downloads.html"; 35 | public static string FileNotFoundURL = "sharpbrowser://storage/errors/notFound.html"; 36 | public static string CannotConnectURL = "sharpbrowser://storage/errors/cannotConnect.html"; 37 | public static string SearchURL = "https://www.google.com/#q="; 38 | 39 | public bool WebSecurity = true; 40 | public bool CrossDomainSecurity = true; 41 | public bool WebGL = true; 42 | 43 | 44 | 45 | public MainForm() { 46 | 47 | Instance = this; 48 | 49 | InitializeComponent(); 50 | 51 | InitBrowser(); 52 | 53 | SetFormTitle(null); 54 | 55 | } 56 | 57 | private void MainForm_Load(object sender, EventArgs e) { 58 | 59 | InitAppIcon(); 60 | InitTooltips(this.Controls); 61 | InitHotkeys(); 62 | 63 | } 64 | 65 | #region App Icon 66 | 67 | /// 68 | /// embedding the resource using the Visual Studio designer results in a blurry icon. 69 | /// the best way to get a non-blurry icon for Windows 7 apps. 70 | /// 71 | private void InitAppIcon() { 72 | assembly = Assembly.GetAssembly(typeof(MainForm)); 73 | Icon = new Icon(GetResourceStream("sharpbrowser.ico"), new Size(64, 64)); 74 | } 75 | 76 | public static Assembly assembly = null; 77 | public Stream GetResourceStream(string filename, bool withNamespace = true) { 78 | try { 79 | return assembly.GetManifestResourceStream("SharpBrowser.Resources." + filename); 80 | } catch (System.Exception ex) { } 81 | return null; 82 | } 83 | 84 | #endregion 85 | 86 | #region Tooltips & Hotkeys 87 | 88 | /// 89 | /// these hotkeys work when the user is focussed on the .NET form and its controls, 90 | /// AND when the user is focussed on the browser (CefSharp portion) 91 | /// 92 | private void InitHotkeys() { 93 | 94 | // browser hotkeys 95 | KeyboardHandler.AddHotKey(this, CloseActiveTab, Keys.W, true); 96 | KeyboardHandler.AddHotKey(this, CloseActiveTab, Keys.Escape, true); 97 | KeyboardHandler.AddHotKey(this, AddBlankWindow, Keys.N, true); 98 | KeyboardHandler.AddHotKey(this, AddBlankTab, Keys.T, true); 99 | KeyboardHandler.AddHotKey(this, RefreshActiveTab, Keys.F5); 100 | KeyboardHandler.AddHotKey(this, OpenDeveloperTools, Keys.F12); 101 | KeyboardHandler.AddHotKey(this, NextTab, Keys.Tab, true); 102 | KeyboardHandler.AddHotKey(this, PrevTab, Keys.Tab, true, true); 103 | 104 | // search hotkeys 105 | KeyboardHandler.AddHotKey(this, OpenSearch, Keys.F, true); 106 | KeyboardHandler.AddHotKey(this, CloseSearch, Keys.Escape); 107 | KeyboardHandler.AddHotKey(this, StopActiveTab, Keys.Escape); 108 | 109 | 110 | } 111 | 112 | /// 113 | /// we activate all the tooltips stored in the Tag property of the buttons 114 | /// 115 | public void InitTooltips(System.Windows.Forms.Control.ControlCollection parent) { 116 | foreach (Control ui in parent) { 117 | Button btn = ui as Button; 118 | if (btn != null) { 119 | if (btn.Tag != null) { 120 | ToolTip tip = new ToolTip(); 121 | tip.ReshowDelay = tip.InitialDelay = 200; 122 | tip.ShowAlways = true; 123 | tip.SetToolTip(btn, btn.Tag.ToString()); 124 | } 125 | } 126 | Panel panel = ui as Panel; 127 | if (panel != null) { 128 | InitTooltips(panel.Controls); 129 | } 130 | } 131 | } 132 | 133 | #endregion 134 | 135 | #region Web Browser & Tabs 136 | 137 | private FATabStripItem newStrip; 138 | private FATabStripItem downloadsStrip; 139 | 140 | private string currentFullURL; 141 | private string currentCleanURL; 142 | private string currentTitle; 143 | 144 | public HostHandler host; 145 | private DownloadHandler dHandler; 146 | private ContextMenuHandler mHandler; 147 | private LifeSpanHandler lHandler; 148 | private KeyboardHandler kHandler; 149 | private RequestHandler rHandler; 150 | 151 | /// 152 | /// this is done just once, to globally initialize CefSharp/CEF 153 | /// 154 | private void InitBrowser() { 155 | 156 | CefSettings settings = new CefSettings(); 157 | 158 | settings.RegisterScheme(new CefCustomScheme { 159 | SchemeName = "sharpbrowser", 160 | SchemeHandlerFactory = new SchemeHandlerFactory() 161 | }); 162 | 163 | settings.UserAgent = UserAgent; 164 | 165 | settings.IgnoreCertificateErrors = true; 166 | 167 | settings.CachePath = GetAppDir("Cache"); 168 | 169 | Cef.Initialize(settings); 170 | 171 | dHandler = new DownloadHandler(this); 172 | lHandler = new LifeSpanHandler(this); 173 | mHandler = new ContextMenuHandler(this); 174 | kHandler = new KeyboardHandler(this); 175 | rHandler = new RequestHandler(this); 176 | 177 | InitDownloads(); 178 | 179 | host = new HostHandler(this); 180 | 181 | AddNewBrowser(tabStrip1, HomepageURL); 182 | 183 | } 184 | 185 | /// 186 | /// this is done every time a new tab is openede 187 | /// 188 | private void ConfigureBrowser(ChromiumWebBrowser browser) { 189 | 190 | BrowserSettings config = new BrowserSettings(); 191 | 192 | config.FileAccessFromFileUrls = (!CrossDomainSecurity).ToCefState(); 193 | config.UniversalAccessFromFileUrls = (!CrossDomainSecurity).ToCefState(); 194 | config.WebSecurity = WebSecurity.ToCefState(); 195 | config.WebGl = WebGL.ToCefState(); 196 | 197 | browser.BrowserSettings = config; 198 | 199 | } 200 | 201 | 202 | private static string GetAppDir(string name) { 203 | string winXPDir = @"C:\Documents and Settings\All Users\Application Data\"; 204 | if (Directory.Exists(winXPDir)) { 205 | return winXPDir + Branding + @"\" + name + @"\"; 206 | } 207 | return @"C:\ProgramData\" + Branding + @"\" + name + @"\"; 208 | 209 | } 210 | 211 | private void LoadURL(string url) { 212 | Uri outUri; 213 | string newUrl = url; 214 | string urlLower = url.Trim().ToLower(); 215 | 216 | // UI 217 | SetTabTitle(CurBrowser, "Loading..."); 218 | 219 | // load page 220 | if (urlLower == "localhost") { 221 | 222 | newUrl = "http://localhost/"; 223 | 224 | } else if (url.CheckIfFilePath() || url.CheckIfFilePath2()) { 225 | 226 | newUrl = url.PathToURL(); 227 | 228 | } else { 229 | 230 | Uri.TryCreate(url, UriKind.Absolute, out outUri); 231 | 232 | if (!(urlLower.StartsWith("http") || urlLower.StartsWith("sharpbrowser"))) { 233 | if (outUri == null || outUri.Scheme != Uri.UriSchemeFile) newUrl = "http://" + url; 234 | } 235 | 236 | if (urlLower.StartsWith("sharpbrowser:") || 237 | 238 | // load URL if it seems valid 239 | (Uri.TryCreate(newUrl, UriKind.Absolute, out outUri) 240 | && ((outUri.Scheme == Uri.UriSchemeHttp || outUri.Scheme == Uri.UriSchemeHttps) && newUrl.Contains(".") || outUri.Scheme == Uri.UriSchemeFile))) { 241 | 242 | } else { 243 | 244 | // run search if unknown URL 245 | newUrl = SearchURL + HttpUtility.UrlEncode(url); 246 | 247 | } 248 | 249 | } 250 | 251 | // load URL 252 | CurBrowser.Load(newUrl); 253 | 254 | // set URL in UI 255 | SetFormURL(newUrl); 256 | 257 | // always enable back btn 258 | EnableBackButton(true); 259 | EnableForwardButton(false); 260 | 261 | } 262 | 263 | private void SetFormTitle(string tabName) { 264 | 265 | if (tabName.CheckIfValid()) { 266 | 267 | this.Text = tabName + " - " + Branding; 268 | currentTitle = tabName; 269 | 270 | } else { 271 | 272 | this.Text = Branding; 273 | currentTitle = "New Tab"; 274 | } 275 | 276 | } 277 | 278 | private void SetFormURL(string URL) { 279 | 280 | currentFullURL = URL; 281 | currentCleanURL = CleanURL(URL); 282 | 283 | TxtURL.Text = currentCleanURL; 284 | 285 | CurTab.CurURL = currentFullURL; 286 | 287 | CloseSearch(); 288 | 289 | } 290 | 291 | private string CleanURL(string url) { 292 | if (url.BeginsWith("about:")) { 293 | return ""; 294 | } 295 | url = url.RemovePrefix("http://"); 296 | url = url.RemovePrefix("https://"); 297 | url = url.RemovePrefix("file://"); 298 | url = url.RemovePrefix("/"); 299 | return url.DecodeURL(); 300 | } 301 | private bool IsBlank(string url) { 302 | return (url == "" || url == "about:blank"); 303 | } 304 | private bool IsBlankOrSystem(string url) { 305 | return (url == "" || url.BeginsWith("about:") || url.BeginsWith("chrome:") || url.BeginsWith("sharpbrowser:")); 306 | } 307 | 308 | public void AddBlankWindow() { 309 | 310 | // open a new instance of the browser 311 | 312 | ProcessStartInfo info = new ProcessStartInfo(Application.ExecutablePath, ""); 313 | //info.WorkingDirectory = workingDir ?? exePath.GetPathDir(true); 314 | info.LoadUserProfile = true; 315 | 316 | info.UseShellExecute = false; 317 | info.RedirectStandardError = true; 318 | info.RedirectStandardOutput = true; 319 | info.RedirectStandardInput = true; 320 | 321 | Process.Start(info); 322 | } 323 | public void AddBlankTab() { 324 | AddNewBrowserTab(""); 325 | this.InvokeOnParent(delegate() { 326 | TxtURL.Focus(); 327 | }); 328 | } 329 | 330 | public ChromiumWebBrowser AddNewBrowserTab(string url, bool focusNewTab = true, string refererUrl = null) { 331 | return (ChromiumWebBrowser)this.Invoke((Func)delegate { 332 | 333 | // check if already exists 334 | foreach (FATabStripItem tab in TabPages.Items) { 335 | SharpTab tab2 = (SharpTab)tab.Tag; 336 | if (tab2 != null && (tab2.CurURL == url)) { 337 | TabPages.SelectedItem = tab; 338 | return tab2.Browser; 339 | } 340 | } 341 | 342 | FATabStripItem tabStrip = new FATabStripItem(); 343 | tabStrip.Title = "New Tab"; 344 | TabPages.Items.Insert(TabPages.Items.Count - 1, tabStrip); 345 | newStrip = tabStrip; 346 | 347 | SharpTab newTab = AddNewBrowser(newStrip, url); 348 | newTab.RefererURL = refererUrl; 349 | if (focusNewTab) timer1.Enabled = true; 350 | return newTab.Browser; 351 | }); 352 | } 353 | private SharpTab AddNewBrowser(FATabStripItem tabStrip, String url) { 354 | if (url == "") url = NewTabURL; 355 | ChromiumWebBrowser browser = new ChromiumWebBrowser(url); 356 | 357 | // set config 358 | ConfigureBrowser(browser); 359 | 360 | // set layout 361 | browser.Dock = DockStyle.Fill; 362 | tabStrip.Controls.Add(browser); 363 | browser.BringToFront(); 364 | 365 | // add events 366 | browser.StatusMessage += Browser_StatusMessage; 367 | browser.LoadingStateChanged += Browser_LoadingStateChanged; 368 | browser.TitleChanged += Browser_TitleChanged; 369 | browser.LoadError += Browser_LoadError; 370 | browser.AddressChanged += Browser_URLChanged; 371 | browser.DownloadHandler = dHandler; 372 | browser.MenuHandler = mHandler; 373 | browser.LifeSpanHandler = lHandler; 374 | browser.KeyboardHandler = kHandler; 375 | browser.RequestHandler = rHandler; 376 | 377 | // new tab obj 378 | SharpTab tab = new SharpTab { 379 | IsOpen = true, 380 | Browser = browser, 381 | Tab = tabStrip, 382 | OrigURL = url, 383 | CurURL = url, 384 | Title = "New Tab", 385 | DateCreated = DateTime.Now 386 | }; 387 | 388 | // save tab obj in tabstrip 389 | tabStrip.Tag = tab; 390 | 391 | if (url.StartsWith("sharpbrowser:")) { 392 | browser.RegisterAsyncJsObject("host", host, true); 393 | } 394 | return tab; 395 | } 396 | 397 | public SharpTab GetTabByBrowser(IWebBrowser browser) { 398 | foreach (FATabStripItem tab2 in TabPages.Items) { 399 | SharpTab tab = (SharpTab)(tab2.Tag); 400 | if (tab != null && tab.Browser == browser) { 401 | return tab; 402 | } 403 | } 404 | return null; 405 | } 406 | 407 | public void RefreshActiveTab() { 408 | CurBrowser.Load(CurBrowser.Address); 409 | } 410 | 411 | public void CloseActiveTab() { 412 | if (CurTab != null/* && TabPages.Items.Count > 2*/) { 413 | 414 | // remove tab and save its index 415 | int index = TabPages.Items.IndexOf(TabPages.SelectedItem); 416 | TabPages.RemoveTab(TabPages.SelectedItem); 417 | 418 | // keep tab at same index focussed 419 | if ((TabPages.Items.Count - 1) > index) { 420 | TabPages.SelectedItem = TabPages.Items[index]; 421 | } 422 | } 423 | } 424 | 425 | private void OnTabClosed(object sender, EventArgs e) { 426 | 427 | } 428 | 429 | private void OnTabClosing(FarsiLibrary.Win.TabStripItemClosingEventArgs e) { 430 | 431 | // exit if invalid tab 432 | if (CurTab == null){ 433 | e.Cancel = true; 434 | return; 435 | } 436 | 437 | // add a blank tab if the very last tab is closed! 438 | if (TabPages.Items.Count <= 2) { 439 | AddBlankTab(); 440 | //e.Cancel = true; 441 | } 442 | 443 | } 444 | 445 | private void StopActiveTab() { 446 | CurBrowser.Stop(); 447 | } 448 | 449 | private bool IsOnFirstTab() { 450 | return TabPages.SelectedItem == TabPages.Items[0]; 451 | } 452 | private bool IsOnLastTab() { 453 | return TabPages.SelectedItem == TabPages.Items[TabPages.Items.Count - 2]; 454 | } 455 | 456 | private int CurIndex { 457 | get { 458 | return TabPages.Items.IndexOf(TabPages.SelectedItem); 459 | } 460 | set { 461 | TabPages.SelectedItem = TabPages.Items[value]; 462 | } 463 | } 464 | private int LastIndex { 465 | get { 466 | return TabPages.Items.Count - 2; 467 | } 468 | } 469 | 470 | private void NextTab() { 471 | if (IsOnLastTab()) { 472 | CurIndex = 0; 473 | } else { 474 | CurIndex++; 475 | } 476 | } 477 | private void PrevTab() { 478 | if (IsOnFirstTab()) { 479 | CurIndex = LastIndex; 480 | } else { 481 | CurIndex--; 482 | } 483 | } 484 | 485 | public ChromiumWebBrowser CurBrowser { 486 | get { 487 | if (TabPages.SelectedItem != null && TabPages.SelectedItem.Tag != null) { 488 | return ((SharpTab)TabPages.SelectedItem.Tag).Browser; 489 | } else { 490 | return null; 491 | } 492 | } 493 | } 494 | 495 | public SharpTab CurTab { 496 | get { 497 | if (TabPages.SelectedItem != null && TabPages.SelectedItem.Tag != null) { 498 | return ((SharpTab)TabPages.SelectedItem.Tag); 499 | } else { 500 | return null; 501 | } 502 | } 503 | } 504 | 505 | public int CurTabLoadingDur { 506 | get { 507 | if (TabPages.SelectedItem != null && TabPages.SelectedItem.Tag != null) { 508 | int loadTime = (int)(DateTime.Now - CurTab.DateCreated).TotalMilliseconds; 509 | return loadTime; 510 | } else { 511 | return 0; 512 | } 513 | } 514 | } 515 | 516 | private void Browser_URLChanged(object sender, AddressChangedEventArgs e) { 517 | InvokeIfNeeded(() => { 518 | 519 | // if current tab 520 | if (sender == CurBrowser) { 521 | 522 | if (!Utils.IsFocussed(TxtURL)) { 523 | SetFormURL(e.Address); 524 | } 525 | 526 | EnableBackButton(CurBrowser.CanGoBack); 527 | EnableForwardButton(CurBrowser.CanGoForward); 528 | 529 | SetTabTitle((ChromiumWebBrowser)sender, "Loading..."); 530 | 531 | BtnRefresh.Visible = false; 532 | BtnStop.Visible = true; 533 | 534 | CurTab.DateCreated = DateTime.Now; 535 | 536 | } 537 | 538 | }); 539 | } 540 | 541 | private void Browser_LoadError(object sender, LoadErrorEventArgs e) { 542 | // ("Load Error:" + e.ErrorCode + ";" + e.ErrorText); 543 | } 544 | 545 | private void Browser_TitleChanged(object sender, TitleChangedEventArgs e) { 546 | InvokeIfNeeded(() => { 547 | 548 | ChromiumWebBrowser browser = (ChromiumWebBrowser)sender; 549 | 550 | SetTabTitle(browser, e.Title); 551 | 552 | }); 553 | } 554 | 555 | private void SetTabTitle(ChromiumWebBrowser browser, string text) { 556 | 557 | text = text.Trim(); 558 | if (IsBlank(text)) { 559 | text = "New Tab"; 560 | } 561 | 562 | // save text 563 | browser.Tag = text; 564 | 565 | // get tab of given browser 566 | FATabStripItem tabStrip = (FATabStripItem)browser.Parent; 567 | tabStrip.Title = text; 568 | 569 | 570 | // if current tab 571 | if (browser == CurBrowser) { 572 | 573 | SetFormTitle(text); 574 | 575 | } 576 | } 577 | 578 | private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e) { 579 | if (sender == CurBrowser) { 580 | 581 | EnableBackButton(e.CanGoBack); 582 | EnableForwardButton(e.CanGoForward); 583 | 584 | if (e.IsLoading) { 585 | 586 | // set title 587 | //SetTabTitle(); 588 | 589 | } else { 590 | 591 | // after loaded / stopped 592 | InvokeIfNeeded(() => { 593 | BtnRefresh.Visible = true; 594 | BtnStop.Visible = false; 595 | }); 596 | } 597 | } 598 | } 599 | 600 | public void InvokeIfNeeded(Action action) { 601 | if (this.InvokeRequired) { 602 | this.BeginInvoke(action); 603 | } else { 604 | action.Invoke(); 605 | } 606 | } 607 | 608 | private void Browser_StatusMessage(object sender, StatusMessageEventArgs e) { 609 | } 610 | 611 | public void WaitForBrowserToInitialize(ChromiumWebBrowser browser) { 612 | while (!browser.IsBrowserInitialized) { 613 | Thread.Sleep(100); 614 | } 615 | } 616 | 617 | private void EnableBackButton(bool canGoBack) { 618 | InvokeIfNeeded(() => BtnBack.Enabled = canGoBack); 619 | } 620 | private void EnableForwardButton(bool canGoForward) { 621 | InvokeIfNeeded(() => BtnForward.Enabled = canGoForward); 622 | } 623 | 624 | private void OnTabsChanged(TabStripItemChangedEventArgs e) { 625 | 626 | 627 | ChromiumWebBrowser browser = null; 628 | try { 629 | browser = ((ChromiumWebBrowser)e.Item.Controls[0]); 630 | } catch (System.Exception ex) { } 631 | 632 | 633 | if (e.ChangeType == FATabStripItemChangeTypes.SelectionChanged) { 634 | if (TabPages.SelectedItem == tabStripAdd) { 635 | AddBlankTab(); 636 | } else { 637 | 638 | browser = CurBrowser; 639 | 640 | SetFormURL(browser.Address); 641 | SetFormTitle(browser.Tag.ConvertToString() ?? "New Tab"); 642 | 643 | 644 | EnableBackButton(browser.CanGoBack); 645 | EnableForwardButton(browser.CanGoForward); 646 | 647 | } 648 | } 649 | 650 | if (e.ChangeType == FATabStripItemChangeTypes.Removed) { 651 | if (e.Item == downloadsStrip) downloadsStrip = null; 652 | if (browser != null) { 653 | browser.Dispose(); 654 | } 655 | } 656 | 657 | if (e.ChangeType == FATabStripItemChangeTypes.Changed) { 658 | if (browser != null) { 659 | if (currentFullURL != "about:blank") { 660 | browser.Focus(); 661 | } 662 | } 663 | } 664 | 665 | } 666 | 667 | private void timer1_Tick(object sender, EventArgs e) { 668 | TabPages.SelectedItem = newStrip; 669 | timer1.Enabled = false; 670 | } 671 | 672 | private void menuCloseTab_Click(object sender, EventArgs e) { 673 | CloseActiveTab(); 674 | } 675 | 676 | private void menuCloseOtherTabs_Click(object sender, EventArgs e) { 677 | List listToClose = new List(); 678 | foreach (FATabStripItem tab in TabPages.Items) { 679 | if (tab != tabStripAdd && tab != TabPages.SelectedItem) listToClose.Add(tab); 680 | } 681 | foreach (FATabStripItem tab in listToClose) { 682 | TabPages.RemoveTab(tab); 683 | } 684 | 685 | } 686 | 687 | public List CancelRequests { 688 | get { 689 | return downloadCancelRequests; 690 | } 691 | } 692 | 693 | private void bBack_Click(object sender, EventArgs e) { 694 | CurBrowser.Back(); 695 | } 696 | 697 | private void bForward_Click(object sender, EventArgs e) { 698 | CurBrowser.Forward(); 699 | } 700 | 701 | private void txtUrl_TextChanged(object sender, EventArgs e) { 702 | 703 | } 704 | 705 | private void bDownloads_Click(object sender, EventArgs e) { 706 | AddNewBrowserTab(DownloadsURL); 707 | } 708 | 709 | private void bRefresh_Click(object sender, EventArgs e) { 710 | RefreshActiveTab(); 711 | } 712 | 713 | private void bStop_Click(object sender, EventArgs e) { 714 | StopActiveTab(); 715 | } 716 | private void TxtURL_KeyDown(object sender, KeyEventArgs e) { 717 | 718 | // if ENTER or CTRL+ENTER pressed 719 | if (e.IsHotkey(Keys.Enter) || e.IsHotkey(Keys.Enter, true)) { 720 | LoadURL(TxtURL.Text); 721 | 722 | // im handling this 723 | e.Handled = true; 724 | e.SuppressKeyPress = true; 725 | 726 | // defocus from url textbox 727 | this.Focus(); 728 | } 729 | 730 | // if full URL copied 731 | if (e.IsHotkey(Keys.C, true) && Utils.IsFullySelected(TxtURL)) { 732 | 733 | // copy the real URL, not the pretty one 734 | Clipboard.SetText(CurBrowser.Address, TextDataFormat.UnicodeText); 735 | 736 | // im handling this 737 | e.Handled = true; 738 | e.SuppressKeyPress = true; 739 | } 740 | } 741 | 742 | private void txtUrl_Click(object sender, EventArgs e) { 743 | if (!Utils.HasSelection(TxtURL)) { 744 | TxtURL.SelectAll(); 745 | } 746 | } 747 | 748 | private void OpenDeveloperTools() { 749 | CurBrowser.ShowDevTools(); 750 | } 751 | 752 | private void tabPages_MouseClick(object sender, MouseEventArgs e) { 753 | /*if (e.Button == System.Windows.Forms.MouseButtons.Right) { 754 | tabPages.GetTabItemByPoint(this.mouse 755 | }*/ 756 | } 757 | 758 | #endregion 759 | 760 | #region Download Queue 761 | 762 | private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { 763 | 764 | // ask user if they are sure 765 | if (DownloadsInProgress()) { 766 | if (MessageBox.Show("Downloads are in progress. Cancel those and exit?", "Confirm exit", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) { 767 | e.Cancel = true; 768 | return; 769 | } 770 | } 771 | 772 | // dispose all browsers 773 | try { 774 | foreach (TabPage tab in TabPages.Items) { 775 | ChromiumWebBrowser browser = (ChromiumWebBrowser)tab.Controls[0]; 776 | browser.Dispose(); 777 | } 778 | } catch (System.Exception ex) { } 779 | 780 | } 781 | 782 | public Dictionary downloads; 783 | public Dictionary downloadNames; 784 | public List downloadCancelRequests; 785 | 786 | /// 787 | /// we must store download metadata in a list, since CefSharp does not 788 | /// 789 | private void InitDownloads() { 790 | 791 | downloads = new Dictionary(); 792 | downloadNames = new Dictionary(); 793 | downloadCancelRequests = new List(); 794 | 795 | } 796 | 797 | public Dictionary Downloads { 798 | get { 799 | return downloads; 800 | } 801 | } 802 | 803 | public void UpdateDownloadItem(DownloadItem item) { 804 | lock (downloads) { 805 | 806 | // SuggestedFileName comes full only in the first attempt so keep it somewhere 807 | if (item.SuggestedFileName != "") { 808 | downloadNames[item.Id] = item.SuggestedFileName; 809 | } 810 | 811 | // Set it back if it is empty 812 | if (item.SuggestedFileName == "" && downloadNames.ContainsKey(item.Id)) { 813 | item.SuggestedFileName = downloadNames[item.Id]; 814 | } 815 | 816 | downloads[item.Id] = item; 817 | 818 | //UpdateSnipProgress(); 819 | } 820 | } 821 | 822 | public string CalcDownloadPath(DownloadItem item) { 823 | 824 | string itemName = item.SuggestedFileName != null ? item.SuggestedFileName.GetAfterLast(".") + " file" : "downloads"; 825 | 826 | string path = null; 827 | if (path != null) { 828 | return path; 829 | } 830 | 831 | return null; 832 | } 833 | 834 | public bool DownloadsInProgress() { 835 | foreach (DownloadItem item in downloads.Values) { 836 | if (item.IsInProgress) { 837 | return true; 838 | } 839 | } 840 | return false; 841 | } 842 | 843 | /// 844 | /// open a new tab with the downloads URL 845 | /// 846 | private void btnDownloads_Click(object sender, EventArgs e) { 847 | OpenDownloadsTab(); 848 | } 849 | 850 | public void OpenDownloadsTab() { 851 | if (downloadsStrip != null && ((ChromiumWebBrowser)downloadsStrip.Controls[0]).Address == DownloadsURL) { 852 | TabPages.SelectedItem = downloadsStrip; 853 | } else { 854 | ChromiumWebBrowser brw = AddNewBrowserTab(DownloadsURL); 855 | downloadsStrip = (FATabStripItem)brw.Parent; 856 | } 857 | } 858 | 859 | #endregion 860 | 861 | #region Search Bar 862 | 863 | bool searchOpen = false; 864 | string lastSearch = ""; 865 | 866 | private void OpenSearch() { 867 | if (!searchOpen) { 868 | searchOpen = true; 869 | InvokeIfNeeded(delegate() { 870 | PanelSearch.Visible = true; 871 | TxtSearch.Text = lastSearch; 872 | TxtSearch.Focus(); 873 | TxtSearch.SelectAll(); 874 | }); 875 | } else { 876 | InvokeIfNeeded(delegate() { 877 | TxtSearch.Focus(); 878 | TxtSearch.SelectAll(); 879 | }); 880 | } 881 | } 882 | private void CloseSearch() { 883 | if (searchOpen) { 884 | searchOpen = false; 885 | InvokeIfNeeded(delegate() { 886 | PanelSearch.Visible = false; 887 | CurBrowser.GetBrowser().StopFinding(true); 888 | }); 889 | } 890 | } 891 | 892 | private void BtnClearSearch_Click(object sender, EventArgs e) { 893 | CloseSearch(); 894 | } 895 | 896 | private void BtnPrevSearch_Click(object sender, EventArgs e) { 897 | FindTextOnPage(false); 898 | } 899 | private void BtnNextSearch_Click(object sender, EventArgs e) { 900 | FindTextOnPage(true); 901 | } 902 | 903 | private void FindTextOnPage(bool next = true) { 904 | bool first = lastSearch != TxtSearch.Text; 905 | lastSearch = TxtSearch.Text; 906 | if (lastSearch.CheckIfValid()) { 907 | CurBrowser.GetBrowser().Find(0, lastSearch, true, false, !first); 908 | } else { 909 | CurBrowser.GetBrowser().StopFinding(true); 910 | } 911 | TxtSearch.Focus(); 912 | } 913 | 914 | private void TxtSearch_TextChanged(object sender, EventArgs e) { 915 | FindTextOnPage(true); 916 | } 917 | 918 | private void TxtSearch_KeyDown(object sender, KeyEventArgs e) { 919 | if (e.IsHotkey(Keys.Enter)) { 920 | FindTextOnPage(true); 921 | } 922 | if (e.IsHotkey(Keys.Enter, true) || e.IsHotkey(Keys.Enter, false, true)) { 923 | FindTextOnPage(false); 924 | } 925 | } 926 | 927 | #endregion 928 | 929 | 930 | 931 | } 932 | } 933 | 934 | /// 935 | /// POCO created for holding data per tab 936 | /// 937 | internal class SharpTab { 938 | 939 | public bool IsOpen; 940 | 941 | public string OrigURL; 942 | public string CurURL; 943 | public string Title; 944 | 945 | public string RefererURL; 946 | 947 | public DateTime DateCreated; 948 | 949 | public FATabStripItem Tab; 950 | public ChromiumWebBrowser Browser; 951 | 952 | } 953 | 954 | /// 955 | /// POCO for holding hotkey data 956 | /// 957 | internal class SharpHotKey { 958 | 959 | public Keys Key; 960 | public int KeyCode; 961 | public bool Ctrl; 962 | public bool Shift; 963 | public bool Alt; 964 | 965 | public Action Callback; 966 | 967 | public SharpHotKey(Action callback, Keys key, bool ctrl = false, bool shift = false, bool alt = false) { 968 | Callback = callback; 969 | Key = key; 970 | KeyCode = (int)key; 971 | Ctrl = ctrl; 972 | Shift = shift; 973 | Alt = alt; 974 | } 975 | 976 | } -------------------------------------------------------------------------------- /src/MainForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 325, 17 122 | 123 | 124 | 125 | 126 | iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 127 | YQUAAAEZSURBVDhPxVRbEQIxDKwDJCABCfzwxXBXCUhAAlJOAgJoDwlIQMJJgGw7LUlf9AfITKcznXST 128 | TbJR6mc2zFppOyltbtkZ5m1/Hod5QwB3Anu2DwWCL8wFJv/MdtdVHxgPZi4xsAAsgY32rECPH7xpuxSz 129 | F4DekdOcqnVywe0jA40fvAOPWgfDp9Gc2hmO5vh2oIa0rAYmmoIagXKoWQuQ+4Y/4e6fpb977u06UuY0 130 | ehSBmro/1Idomg2nHJ3lQ4NYM0ktDJD0mkltidIqoaZCEGzcAkgAQaNlglU6aiVAH2ByskM2MNx+DqVK 131 | wpIoUIZayjqtbR/RjIDo0/c1Q8frGTOtE03szKIBBIcbnP3CSJfAw72HMnx75l9XQGc65ZPKTwAAAABJ 132 | RU5ErkJggg== 133 | 134 | 135 | 136 | 137 | iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 138 | YQUAAAEtSURBVDhPvZTbDcIwDEW9ASMwAiMwAiMwAiMwAr+8pI7ACB2BETpCRii+lZ06r8ZfVIpUqHN8 139 | r+OY6F/Pg2j3Ijrxuj6Jxmwd3ToA4nVjwLy1OOZzJzpsggFjyLcHM99DEyrKJg3m35PYTewBAHUWyu9l 140 | CaRGanNoWQGQY4N1geRJvAQtMGT3wgBSMA4w7uMPZ2P1XANWlA1Sc1W7upJaLQrfRPscWINpDO8ZxNlq 141 | W4tc1IJ3bcEAtWKiEHMgo1XXgyHW9mzcq1msQg8MABWTuOM/L3ooqKEXJodStpptG8lo+6zZk7Z+SduI 142 | 9NqVa8KyVksbG8CKzVBk5TiUJLt6cy1uOSC5p7H7pa5QbseXnUIBSj0TB4De+Jq648tmwgSRHksGAW5G 143 | V5V7+joCf9RfGvLkc7XEAAAAAElFTkSuQmCC 144 | 145 | 146 | 147 | 148 | iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 149 | YQUAAAEWSURBVDhPvZRNEcIwEIXjAAlIQAIXTgxNJCABCUhAAhIQQFIkIAEJlVB22ybk522bXtiZXDrJ 150 | 1/dedqPUX+rotsrYhzKuo9WXy76Utpc6LU1rZFAOp58enhsZrO0ZK2Kl9i2qhcSm3YuwU7sbzhh3h3u0 151 | u5ZMQ7mgvPLNGNql1lkBDJ8zI6t5TgiaXBKrEIHDRXTK2/beCig5DBXb1e5W1w4gUwjky1lTxn2CO6yQ 152 | Wqe2xux/zR/Osc35DHuajPRHOYwvL9Q4HWDEpm+LMNpXZB9nEcPzPiyUTT/l+U9KnBSaV18SDE7K3Gjx 153 | yzP2Knh9QOMnSmsuKESyBPPk0T5+CAYYgarfw1guB80H2a5faxu/tqelfV9C2Z1KUaQMeQAAAABJRU5E 154 | rkJggg== 155 | 156 | 157 | 158 | 159 | iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 160 | YQUAAAEHSURBVDhPvVTbDcIwEMsGjMAIjMAPX4gmIzACIzACI3QEBiApI3QERmCEckfaKJf6+vjpSZEq 161 | wTm2a9eYTeYc9sb5p3HhS6cbH/821t+WcakapwOV4HTp6bXTga2/YkbhQ4xblS1ErJqjstAmFo6kIgts 162 | uI8x4Z+JVS6Jn6Gn5LWQfmkOwHgJxhRcqBXATr4kpixuRswUuWmPfk8j5BZgU9kolUFAzuCayZVBwMWB 163 | 7bchoA0P4SHnMZ8oTWnNEHayKk1sh6zYWlAmJcZxGxaBgm7T3sh7rSk5U62asClTwWU5MavAx7mYlS8I 164 | V62XPQc2mBrl6zXjL8/aeP2x2WheZLnD4cu2nB+yNJ4saRMhYwAAAABJRU5ErkJggg== 165 | 166 | 167 | 168 | 238, 17 169 | 170 | 171 | 172 | iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 173 | YQUAAADWSURBVDhPY2AY8sDIyOi+sbHxfxAGsSn2EMwwGD1qIOEQAIYVPxCvB+L9UAyOECQME19vaGio 174 | T9BEkIHA2HyPHhnY+EQZCLIRpJAIQ+MJug5ZAQFDSTMMZjC6oSBXE+VNoCJ5YBjlAzXUI2OQZpihMMOg 175 | fBR1IL0gM+A+BCqejy3QodmOH6QYaikowuBZEVkPyAy4gTiSBzz/GhgY+IMwLsOgBu8nykAcyQeUFkFp 176 | FVRgzIdaRL6BQAP6QWENtSweSJ8H+ZJgGBKTuJGKNkQYkpRA6a0YAGJH+ATjKEoQAAAAAElFTkSuQmCC 177 | 178 | 179 | 180 | 181 | iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 182 | YQUAAACfSURBVEhLY2AYBaMhMExDwHefP4PfnnyifAdSB1JPEvDbE8/gv/c/FM/Hq9d/73y4Wp99+sTZ 183 | 47VXHskC/BYhWwBx1HsGl938xFnkv+c8QYswLQBZgt/XKLaDXIPPIootgNmG26L7BH1JXHhBVeG2CBZX 184 | JAYRLtvxWgSMO6IjmpD3sFpETQuwxhEtLECxCJhMqRZEhIJwVH40BAZFCAAAcuCqYT80zm4AAAAASUVO 185 | RK5CYII= 186 | 187 | 188 | 189 | 190 | iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 191 | YQUAAACXSURBVEhLY2AYBaMhMHhCwG9PPG0d4793PoP/3v9APJ82FiEsAFlCA4swLaCyRaA4gLgcF6Yw 192 | 6AhbQKGPsFqw5zyD1155Bn8gjekzEn2EywKX3fzgVAWiKbII7FL08Ae6HGYBLO3issh3nz1xydtvTz7C 193 | IiwW4LaI1CDb2w8MkvUYPkB3JsRH6xn8gOpHwWgIjIYA2SEAAOtGqKV2Jt9uAAAAAElFTkSuQmCC 194 | 195 | 196 | 197 | 198 | iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 199 | YQUAAADTSURBVEhL7ZTBDYMwDEW9QUfoCIzQS09VCyN0lI7CICWs0FEyAs0nJBTsNDJCnGIpQjgJz/62 200 | ISpWFDhegev7RE33ocZYuveVGAD82Me5TeYBw7Q4KALimVbHuZnzD4CDOABnLCF7ldXdUwR5v5MoZuAB 201 | KUmzUBkUMpufmwEhghwI+7tYbV6CdAPBv4vJRU53nRr6HzCD1F0VIkkDWi6dmyk1aJx21qauBlORxWbQ 202 | gjjE0qO/LOTG+yIQLQRfG6de8e9SyxVCxsXcoCEYrGJFgbUCX35H/c2nacnTAAAAAElFTkSuQmCC 203 | 204 | 205 | -------------------------------------------------------------------------------- /src/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace SharpBrowser 7 | { 8 | static class Program 9 | { 10 | /// 11 | /// The main entry point for the application. 12 | /// 13 | [STAThread] 14 | static void Main() 15 | { 16 | Application.EnableVisualStyles(); 17 | Application.SetCompatibleTextRenderingDefault(false); 18 | Application.Run(new MainForm()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SharpBrowser")] 9 | [assembly: AssemblyDescription("Sharp Browser Project")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Sharp Browser Project")] 12 | [assembly: AssemblyProduct("SharpBrowser")] 13 | [assembly: AssemblyCopyright("Copyright © 2016 Sharp Browser Project")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("ef19e884-45b7-4cde-8b5a-4d2bcede2e0c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("2.0.0.0")] 36 | [assembly: AssemblyFileVersion("2.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.17929 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SharpBrowser.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SharpBrowser.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.17929 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SharpBrowser.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Resources/blankpage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/src/Resources/blankpage.png -------------------------------------------------------------------------------- /src/Resources/sharpbrowser.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/src/Resources/sharpbrowser.ico -------------------------------------------------------------------------------- /src/SharpBrowser.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Debug 11 | AnyCPU 12 | {703472B8-B275-4F3F-95B6-426036F8462E} 13 | WinExe 14 | Properties 15 | SharpBrowser 16 | SharpBrowser 17 | v4.5.2 18 | 512 19 | 20 | 21 | true 22 | 23 | 24 | 25 | 26 | x86 27 | true 28 | pdbonly 29 | false 30 | bin\ 31 | DEBUG;TRACE 32 | prompt 33 | 4 34 | false 35 | 36 | 37 | x86 38 | pdbonly 39 | true 40 | bin\ 41 | TRACE 42 | prompt 43 | 4 44 | false 45 | 46 | 47 | true 48 | bin\ 49 | DEBUG;TRACE 50 | full 51 | x86 52 | prompt 53 | MinimumRecommendedRules.ruleset 54 | 55 | 56 | bin\ 57 | TRACE 58 | true 59 | pdbonly 60 | x86 61 | prompt 62 | MinimumRecommendedRules.ruleset 63 | 64 | 65 | Resources\sharpbrowser.ico 66 | 67 | 68 | SharpBrowser.Program 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Form 82 | 83 | 84 | MainForm.cs 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | MainForm.cs 93 | 94 | 95 | ResXFileCodeGenerator 96 | Resources.Designer.cs 97 | Designer 98 | 99 | 100 | True 101 | Resources.resx 102 | True 103 | 104 | 105 | Designer 106 | 107 | 108 | 109 | SettingsSingleFileGenerator 110 | Settings.Designer.cs 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | bin\CefSharp.dll 119 | 120 | 121 | bin\CefSharp.Core.dll 122 | 123 | 124 | bin\CefSharp.WinForms.dll 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | bin\TabStrip.dll 135 | 136 | 137 | 138 | 139 | 140 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 157 | -------------------------------------------------------------------------------- /src/SharpBrowser.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpBrowser", "SharpBrowser.csproj", "{703472B8-B275-4F3F-95B6-426036F8462E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x86 = Debug|x86 12 | Release|Any CPU = Release|Any CPU 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {703472B8-B275-4F3F-95B6-426036F8462E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {703472B8-B275-4F3F-95B6-426036F8462E}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {703472B8-B275-4F3F-95B6-426036F8462E}.Debug|x86.ActiveCfg = Debug|x86 19 | {703472B8-B275-4F3F-95B6-426036F8462E}.Debug|x86.Build.0 = Debug|x86 20 | {703472B8-B275-4F3F-95B6-426036F8462E}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {703472B8-B275-4F3F-95B6-426036F8462E}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {703472B8-B275-4F3F-95B6-426036F8462E}.Release|x86.ActiveCfg = Release|x86 23 | {703472B8-B275-4F3F-95B6-426036F8462E}.Release|x86.Build.0 = Release|x86 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /src/SharpBrowser.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/src/SharpBrowser.zip -------------------------------------------------------------------------------- /src/Utils/FileIconUtils.cs: -------------------------------------------------------------------------------- 1 |  2 | 3 | using System; 4 | using System.Drawing; 5 | using System.Runtime.InteropServices; 6 | using Microsoft.Win32; 7 | using System.Reflection; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | 11 | namespace SharpBrowser { 12 | 13 | /// 14 | /// Two constants extracted from the FileInfoFlags, the only that are 15 | /// meaningfull for the user of this class. 16 | /// 17 | public enum FileIconSize : int { 18 | Large = 0x000000000, 19 | Small = 0x000000001 20 | } 21 | 22 | public static class FileIconUtils { 23 | 24 | 25 | // TOP LEVEL API 26 | 27 | public static MemoryStream GetFileIcon(string name, FileIconSize size) { 28 | Icon icon = FileIconUtils.IconFromExtension(name.GetAfter("."), size); 29 | using (icon) { 30 | using (var bmp = icon.ToBitmap()) { 31 | MemoryStream ms = new MemoryStream(); 32 | bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png); 33 | ms.Seek(0, SeekOrigin.Begin); 34 | return ms; 35 | } 36 | } 37 | } 38 | 39 | 40 | #region Custom exceptions class 41 | 42 | public class IconNotFoundException : Exception { 43 | public IconNotFoundException(string fileName, int index) 44 | : base(string.Format("Icon with Id = {0} wasn't found in file {1}", index, fileName)) { 45 | } 46 | } 47 | 48 | public class UnableToExtractIconsException : Exception { 49 | public UnableToExtractIconsException(string fileName, int firstIconIndex, int iconCount) 50 | : base(string.Format("Tryed to extract {2} icons starting from the one with id {1} from the \"{0}\" file but failed", fileName, firstIconIndex, iconCount)) { 51 | } 52 | } 53 | 54 | #endregion 55 | 56 | #region DllImports 57 | 58 | /// 59 | /// Contains information about a file object. 60 | /// 61 | struct SHFILEINFO { 62 | /// 63 | /// Handle to the icon that represents the file. You are responsible for 64 | /// destroying this handle with DestroyIcon when you no longer need it. 65 | /// 66 | public IntPtr hIcon; 67 | 68 | /// 69 | /// Index of the icon image within the system image list. 70 | /// 71 | public IntPtr iIcon; 72 | 73 | /// 74 | /// Array of values that indicates the attributes of the file object. 75 | /// For information about these values, see the IShellFolder::GetAttributesOf 76 | /// method. 77 | /// 78 | public uint dwAttributes; 79 | 80 | /// 81 | /// String that contains the name of the file as it appears in the Microsoft 82 | /// Windows Shell, or the path and file name of the file that contains the 83 | /// icon representing the file. 84 | /// 85 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] 86 | public string szDisplayName; 87 | 88 | /// 89 | /// String that describes the type of file. 90 | /// 91 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] 92 | public string szTypeName; 93 | }; 94 | 95 | [Flags] 96 | enum FileInfoFlags : int { 97 | /// 98 | /// Retrieve the handle to the icon that represents the file and the index 99 | /// of the icon within the system image list. The handle is copied to the 100 | /// hIcon member of the structure specified by psfi, and the index is copied 101 | /// to the iIcon member. 102 | /// 103 | SHGFI_ICON = 0x000000100, 104 | /// 105 | /// Indicates that the function should not attempt to access the file 106 | /// specified by pszPath. Rather, it should act as if the file specified by 107 | /// pszPath exists with the file attributes passed in dwFileAttributes. 108 | /// 109 | SHGFI_USEFILEATTRIBUTES = 0x000000010 110 | } 111 | 112 | /// 113 | /// Creates an array of handles to large or small icons extracted from 114 | /// the specified executable file, dynamic-link library (DLL), or icon 115 | /// file. 116 | /// 117 | /// 118 | /// Name of an executable file, DLL, or icon file from which icons will 119 | /// be extracted. 120 | /// 121 | /// 122 | /// 123 | /// Specifies the zero-based index of the first icon to extract. For 124 | /// example, if this value is zero, the function extracts the first 125 | /// icon in the specified file. 126 | /// 127 | /// 128 | /// If this value is �1 and and 129 | /// are both NULL, the function returns 130 | /// the total number of icons in the specified file. If the file is an 131 | /// executable file or DLL, the return value is the number of 132 | /// RT_GROUP_ICON resources. If the file is an .ico file, the return 133 | /// value is 1. 134 | /// 135 | /// 136 | /// Windows 95/98/Me, Windows NT 4.0 and later: If this value is a 137 | /// negative number and either or 138 | /// is not NULL, the function begins by 139 | /// extracting the icon whose resource identifier is equal to the 140 | /// absolute value of . For example, use -3 141 | /// to extract the icon whose resource identifier is 3. 142 | /// 143 | /// 144 | /// 145 | /// An array of icon handles that receives handles to the large icons 146 | /// extracted from the file. If this parameter is NULL, no large icons 147 | /// are extracted from the file. 148 | /// 149 | /// 150 | /// An array of icon handles that receives handles to the small icons 151 | /// extracted from the file. If this parameter is NULL, no small icons 152 | /// are extracted from the file. 153 | /// 154 | /// 155 | /// Specifies the number of icons to extract from the file. 156 | /// 157 | /// 158 | /// If the parameter is -1, the 159 | /// parameter is NULL, and the 160 | /// parameter is NULL, then the return 161 | /// value is the number of icons contained in the specified file. 162 | /// Otherwise, the return value is the number of icons successfully 163 | /// extracted from the file. 164 | /// 165 | [DllImport("Shell32", CharSet = CharSet.Auto)] 166 | extern static int ExtractIconEx( 167 | [MarshalAs(UnmanagedType.LPTStr)] 168 | string lpszFile, 169 | int nIconIndex, 170 | IntPtr[] phIconLarge, 171 | IntPtr[] phIconSmall, 172 | int nIcons); 173 | 174 | [DllImport("Shell32", CharSet = CharSet.Auto)] 175 | extern static IntPtr SHGetFileInfo( 176 | string pszPath, 177 | int dwFileAttributes, 178 | out SHFILEINFO psfi, 179 | int cbFileInfo, 180 | FileInfoFlags uFlags); 181 | 182 | #endregion 183 | 184 | #region ExtractIcon-like functions 185 | 186 | public static void ExtractEx(string fileName, List largeIcons, 187 | List smallIcons, int firstIconIndex, int iconCount) { 188 | /* 189 | * Memory allocations 190 | */ 191 | 192 | IntPtr[] smallIconsPtrs = null; 193 | IntPtr[] largeIconsPtrs = null; 194 | 195 | if (smallIcons != null) { 196 | smallIconsPtrs = new IntPtr[iconCount]; 197 | } 198 | if (largeIcons != null) { 199 | largeIconsPtrs = new IntPtr[iconCount]; 200 | } 201 | 202 | /* 203 | * Call to native Win32 API 204 | */ 205 | 206 | int apiResult = ExtractIconEx(fileName, firstIconIndex, largeIconsPtrs, smallIconsPtrs, iconCount); 207 | if (apiResult != iconCount) { 208 | throw new UnableToExtractIconsException(fileName, firstIconIndex, iconCount); 209 | } 210 | 211 | /* 212 | * Fill lists 213 | */ 214 | 215 | if (smallIcons != null) { 216 | smallIcons.Clear(); 217 | foreach (IntPtr actualIconPtr in smallIconsPtrs) { 218 | smallIcons.Add(Icon.FromHandle(actualIconPtr)); 219 | } 220 | } 221 | if (largeIcons != null) { 222 | largeIcons.Clear(); 223 | foreach (IntPtr actualIconPtr in largeIconsPtrs) { 224 | largeIcons.Add(Icon.FromHandle(actualIconPtr)); 225 | } 226 | } 227 | } 228 | 229 | public static List ExtractEx(string fileName, FileIconSize size, 230 | int firstIconIndex, int iconCount) { 231 | List iconList = new List(); 232 | 233 | switch (size) { 234 | case FileIconSize.Large: 235 | ExtractEx(fileName, iconList, null, firstIconIndex, iconCount); 236 | break; 237 | 238 | case FileIconSize.Small: 239 | ExtractEx(fileName, null, iconList, firstIconIndex, iconCount); 240 | break; 241 | 242 | default: 243 | throw new ArgumentOutOfRangeException("size"); 244 | } 245 | 246 | return iconList; 247 | } 248 | 249 | public static void Extract(string fileName, List largeIcons, List smallIcons) { 250 | int iconCount = GetIconsCountInFile(fileName); 251 | ExtractEx(fileName, largeIcons, smallIcons, 0, iconCount); 252 | } 253 | 254 | public static List Extract(string fileName, FileIconSize size) { 255 | int iconCount = GetIconsCountInFile(fileName); 256 | return ExtractEx(fileName, size, 0, iconCount); 257 | } 258 | 259 | public static Icon ExtractOne(string fileName, int index, FileIconSize size) { 260 | try { 261 | List iconList = ExtractEx(fileName, size, index, 1); 262 | return iconList[0]; 263 | } catch (UnableToExtractIconsException) { 264 | throw new IconNotFoundException(fileName, index); 265 | } 266 | } 267 | 268 | public static void ExtractOne(string fileName, int index, 269 | out Icon largeIcon, out Icon smallIcon) { 270 | List smallIconList = new List(); 271 | List largeIconList = new List(); 272 | try { 273 | ExtractEx(fileName, largeIconList, smallIconList, index, 1); 274 | largeIcon = largeIconList[0]; 275 | smallIcon = smallIconList[0]; 276 | } catch (UnableToExtractIconsException) { 277 | throw new IconNotFoundException(fileName, index); 278 | } 279 | } 280 | 281 | #endregion 282 | 283 | 284 | 285 | /// 286 | /// Get the number of icons in the specified file. 287 | /// 288 | /// Full path of the file to look for. 289 | /// 290 | static int GetIconsCountInFile(string fileName) { 291 | return ExtractIconEx(fileName, -1, null, null, 0); 292 | } 293 | 294 | //this will look throw the registry 295 | //to find if the Extension have an icon. 296 | public static Icon IconFromExtension(string extension, 297 | FileIconSize size) { 298 | // Add the '.' to the extension if needed 299 | if (extension[0] != '.') extension = '.' + extension; 300 | extension = extension.ToLower(); 301 | 302 | //opens the registry for the wanted key. 303 | RegistryKey Root = Registry.ClassesRoot; 304 | RegistryKey ExtensionKey = Root.OpenSubKey(extension); 305 | ExtensionKey.GetValueNames(); 306 | RegistryKey ApplicationKey = 307 | Root.OpenSubKey(ExtensionKey.GetValue("").ToString()); 308 | 309 | //gets the name of the file that have the icon. 310 | string IconLocation = 311 | ApplicationKey.OpenSubKey("DefaultIcon").GetValue("").ToString(); 312 | string[] IconPath = IconLocation.Split(','); 313 | 314 | if (IconPath[1] == null) IconPath[1] = "0"; 315 | IntPtr[] Large = new IntPtr[1], Small = new IntPtr[1]; 316 | 317 | //extracts the icon from the file. 318 | ExtractIconEx(IconPath[0], 319 | Convert.ToInt16(IconPath[1]), Large, Small, 1); 320 | return size == FileIconSize.Large ? 321 | Icon.FromHandle(Large[0]) : Icon.FromHandle(Small[0]); 322 | } 323 | 324 | /// 325 | /// Parse strings in registry who contains the name of the icon and 326 | /// the index of the icon an return both parts. 327 | /// 328 | /// The full string in the form "path,index" as found in registry. 329 | /// The "path" part of the string. 330 | /// The "index" part of the string. 331 | public static void ExtractInformationsFromRegistryString( 332 | string regString, out string fileName, out int index) { 333 | if (regString == null) { 334 | throw new ArgumentNullException("regString"); 335 | } 336 | if (regString.Length == 0) { 337 | throw new ArgumentException("The string should not be empty.", "regString"); 338 | } 339 | 340 | index = 0; 341 | string[] strArr = regString.Replace("\"", "").Split(','); 342 | fileName = strArr[0].Trim(); 343 | if (strArr.Length > 1) { 344 | int.TryParse(strArr[1].Trim(), out index); 345 | } 346 | } 347 | 348 | } 349 | 350 | } -------------------------------------------------------------------------------- /src/Utils/MiscUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Forms; 9 | using CefSharp; 10 | 11 | namespace SharpBrowser { 12 | internal static class Utils { 13 | 14 | public static bool IsFocussed(TextBox tb) { 15 | return tb.Focused; 16 | } 17 | 18 | public static void AddHotKey(Form form, Action function, Keys key, bool ctrl = false, bool shift = false, bool alt = false) { 19 | form.KeyPreview = true; 20 | form.KeyDown += delegate(object sender, KeyEventArgs e) { 21 | if (e.IsHotkey(key, ctrl, shift, alt)) { 22 | function(); 23 | } 24 | }; 25 | } 26 | 27 | public static bool IsFullySelected(TextBox tb) { 28 | return tb.SelectionLength == tb.TextLength; 29 | } 30 | public static bool HasSelection(TextBox tb) { 31 | return tb.SelectionLength > 0; 32 | } 33 | 34 | 35 | public static bool CheckIfFilePath(this string path) { 36 | 37 | if (path[1] == ':') { 38 | if (path[2] == '\\') { 39 | if (Char.IsLetter(path[0])) { 40 | return true; 41 | } 42 | } 43 | } 44 | return false; 45 | } 46 | 47 | public static bool CheckIfFilePath2(this string path) { 48 | 49 | if (path[1] == ':') { 50 | if (path[2] == '/') { 51 | if (Char.IsLetter(path[0])) { 52 | return true; 53 | } 54 | } 55 | } 56 | return false; 57 | } 58 | 59 | public static string GetAfter(this string text, string find, int startAt = 0, bool returnAll = false, bool forward = true) { 60 | if (text == null) { return returnAll ? text : ""; } 61 | int idx; 62 | if (!forward) { 63 | idx = text.LastIndexOf(find, text.Length - startAt, StringComparison.Ordinal); 64 | } else { 65 | idx = text.IndexOf(find, startAt, StringComparison.Ordinal); 66 | } 67 | if (idx == -1) { return returnAll ? text : ""; } 68 | idx += find.Length; 69 | return text.Substring(idx); 70 | } 71 | 72 | public static string GetAfterLast(this string text, string find, bool returnAll = false) { 73 | int idx = text.LastIndexOf(find, StringComparison.Ordinal); 74 | if (idx == -1) { 75 | return returnAll ? text : ""; 76 | } 77 | idx += find.Length; 78 | return text.Substring(idx); 79 | } 80 | 81 | public static bool SupportedInFilePath(this char c) { 82 | return !(c == '?' || c == '\'' || c == '\"' || c == '/' || c == '\\' || c == ';' || c == ':' || c == '&' || c == '*' || c == '<' || c == '>' || c == '|' || c == '{' || c == '}' || c == '[' || c == ']' || c == '(' || c == ')'); 83 | } 84 | 85 | public static string ChangePathSlash(this string filePath, string slash) { 86 | if (slash == "\\") { 87 | if (filePath.Contains('/')) { 88 | return filePath.Replace("/", "\\"); 89 | } 90 | } 91 | if (slash == "/") { 92 | if (filePath.Contains('\\')) { 93 | return filePath.Replace("\\", "/"); 94 | } 95 | } 96 | return filePath; 97 | } 98 | public static string FileURLToPath(this string url) { 99 | return url.RemovePrefix("file:///").ChangePathSlash(@"\").DecodeURLForFilepath(); 100 | } 101 | 102 | public static bool FileNotExists(this string path) { 103 | return !File.Exists(path); 104 | } 105 | 106 | 107 | public static string ConvertToString(this object o) { 108 | if (o is string) { 109 | return o as string; 110 | } 111 | return null; 112 | } 113 | public static bool CheckIfValid(this string text, bool trimAndCheck = false) { 114 | return text != null && text.Length > 0; 115 | } 116 | 117 | public static void InvokeOnParent(this Control control, MethodInvoker method) { 118 | Control parent = control.Parent == null ? control : control.Parent; 119 | if (parent.IsHandleCreated) { 120 | parent.Invoke(method); 121 | return; 122 | } 123 | } 124 | 125 | public static bool IsHotkey(this KeyEventArgs eventData, Keys key, bool ctrl = false, bool shift = false, bool alt = false) { 126 | return eventData.KeyCode == key && eventData.Control == ctrl && eventData.Shift == shift && eventData.Alt == alt; 127 | } 128 | 129 | public static CefState ToCefState(this bool value) { 130 | return value ? CefState.Enabled : CefState.Disabled; 131 | } 132 | public static bool IsBitmaskOn(this int num, int bitmask) { 133 | return (num & bitmask) != 0; 134 | } 135 | 136 | public static bool BeginsWith(this string str, string beginsWith, bool caseSensitive = true) { 137 | if (beginsWith.Length > str.Length) { 138 | return false; 139 | } 140 | if (beginsWith.Length == str.Length) { 141 | return String.Equals(beginsWith, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); 142 | } 143 | return str.LastIndexOf(beginsWith, beginsWith.Length - 1, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase) == 0; 144 | } 145 | public static string RemovePrefix(this string str, string prefix, bool caseSensitive = true) { 146 | if (str.Length >= prefix.Length && str.BeginsWith(prefix, caseSensitive)) { 147 | return str.Substring(prefix.Length); 148 | } 149 | return str; 150 | } 151 | 152 | 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/Utils/URLUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpBrowser { 8 | internal static class URLUtils { 9 | 10 | public static string PathToURL(this string filePath, string removeBaseDir = null) { 11 | 12 | if (!filePath.CheckIfValid()) { 13 | return ""; 14 | } 15 | 16 | return @"file:///" + filePath.Replace(@"\", "/"); 17 | } 18 | 19 | /// 20 | /// checks if URL starts with file: 21 | /// 22 | public static bool IsURLOfflineFile(this string url) { 23 | return url.StartsWith("file://", StringComparison.Ordinal); 24 | } 25 | /// 26 | /// checks if URL is localhost 27 | /// 28 | public static bool IsURLLocalhost(this string url) { 29 | return url.BeginsWith("http://localhost") || url.BeginsWith("localhost"); 30 | } 31 | 32 | /// 33 | /// UrlDecodes a string 34 | /// 35 | public static string DecodeURL(this string url) { 36 | if (url == null) { 37 | return null; 38 | } 39 | int length = url.Length; 40 | UrlDecoder decoder = new UrlDecoder(length, Encoding.UTF8); 41 | 42 | // per char 43 | for (int i = 0; i < length; i++) { 44 | char ch = url[i]; 45 | 46 | 47 | // PLUS char converts to SPACE 48 | if (ch == '+') { 49 | ch = ' '; 50 | 51 | // SPECIAL chars encoded in "%20" format 52 | } else if ((ch == '%') && (i < (length - 2))) { 53 | 54 | // unicode char (4 digit hex) 55 | if ((url[i + 1] == 'u') && (i < (length - 5))) { 56 | int num3 = HexToInt(url[i + 2]); 57 | int num4 = HexToInt(url[i + 3]); 58 | int num5 = HexToInt(url[i + 4]); 59 | int num6 = HexToInt(url[i + 5]); 60 | if (((num3 < 0) || (num4 < 0)) || ((num5 < 0) || (num6 < 0))) { 61 | goto Label_010B; 62 | } 63 | ch = (char)((((num3 << 12) | (num4 << 8)) | (num5 << 4)) | num6); 64 | i += 5; 65 | decoder.AddChar(ch); 66 | continue; 67 | } 68 | 69 | // ascii char (2 digit hex) 70 | int num7 = HexToInt(url[i + 1]); 71 | int num8 = HexToInt(url[i + 2]); 72 | if ((num7 >= 0) && (num8 >= 0)) { 73 | byte b = (byte)((num7 << 4) | num8); 74 | i += 2; 75 | decoder.AddByte(b); 76 | continue; 77 | } 78 | 79 | } 80 | Label_010B: 81 | if ((ch & 0xff80) == 0) { 82 | decoder.AddByte((byte)ch); 83 | } else { 84 | decoder.AddChar(ch); 85 | } 86 | } 87 | return decoder.GetString(); 88 | } 89 | 90 | public static int HexToInt(this char hex) { 91 | if ((hex >= '0') && (hex <= '9')) { 92 | return (hex - '0'); 93 | } 94 | if ((hex >= 'a') && (hex <= 'f')) { 95 | return ((hex - 'a') + 10); 96 | } 97 | if ((hex >= 'A') && (hex <= 'F')) { 98 | return ((hex - 'A') + 10); 99 | } 100 | return -1; 101 | } 102 | 103 | private class UrlDecoder { 104 | private int _bufferSize; 105 | private byte[] _byteBuffer; 106 | private char[] _charBuffer; 107 | private Encoding _encoding; 108 | private int _numBytes; 109 | private int _numChars; 110 | 111 | public bool forFilePaths = false; 112 | 113 | internal UrlDecoder(int bufferSize, Encoding encoding) { 114 | this._bufferSize = bufferSize; 115 | this._encoding = encoding; 116 | this._charBuffer = new char[bufferSize]; 117 | } 118 | 119 | internal void AddByte(byte b) { 120 | if (this._byteBuffer == null) { 121 | this._byteBuffer = new byte[this._bufferSize]; 122 | } 123 | this._byteBuffer[this._numBytes++] = b; 124 | } 125 | 126 | internal void AddChar(char ch, bool checkChar = false) { 127 | if (this._numBytes > 0) { 128 | this.FlushBytes(); 129 | } 130 | 131 | // ADD CHAR AS HEX .. IF NOT SUPPORTED IN FILEPATHS 132 | if (checkChar && forFilePaths) { 133 | if (!ch.SupportedInFilePath()) { 134 | AddChar('_'); 135 | AddString("0x" + ((int)ch).ToString("X")); 136 | AddChar('_'); 137 | return; 138 | } 139 | } 140 | 141 | this._charBuffer[this._numChars++] = ch; 142 | } 143 | internal void AddString(string str) { 144 | if (this._numBytes > 0) { 145 | this.FlushBytes(); 146 | } 147 | foreach (char ch in str) { 148 | this._charBuffer[this._numChars++] = ch; 149 | } 150 | } 151 | 152 | public void FlushBytes(bool checkChar = false) { 153 | if (this._numBytes > 0) { 154 | 155 | if (checkChar && forFilePaths) { 156 | 157 | char[] newChars = this._encoding.GetChars(this._byteBuffer, 0, this._numBytes); 158 | this._numBytes = 0; 159 | 160 | foreach (char ch in newChars) { 161 | AddChar(ch); 162 | } 163 | 164 | } else { 165 | 166 | this._numChars += this._encoding.GetChars(this._byteBuffer, 0, this._numBytes, this._charBuffer, this._numChars); 167 | this._numBytes = 0; 168 | 169 | } 170 | } 171 | } 172 | 173 | internal string GetString() { 174 | if (this._numBytes > 0) { 175 | this.FlushBytes(); 176 | } 177 | if (this._numChars > 0) { 178 | return new string(this._charBuffer, 0, this._numChars); 179 | } 180 | return string.Empty; 181 | } 182 | } 183 | 184 | /// 185 | /// UrlDecodes a string except chars forbidden in Windows filepaths 186 | /// 187 | public static string DecodeURLForFilepath(this string url) { 188 | 189 | 190 | if (url == null) { 191 | return null; 192 | } 193 | 194 | int length = url.Length; 195 | UrlDecoder decoder = new UrlDecoder(length * 10, Encoding.UTF8); 196 | decoder.forFilePaths = true; 197 | 198 | 199 | // per char 200 | for (int i = 0; i < length; i++) { 201 | char ch = url[i]; 202 | 203 | 204 | // PLUS char converts to SPACE 205 | if (ch == '+') { 206 | ch = ' '; 207 | 208 | // SPECIAL chars encoded in "%20" format 209 | } else if ((ch == '%') && (i < (length - 2))) { 210 | 211 | // unicode char (4 digit hex) 212 | if ((url[i + 1] == 'u') && (i < (length - 5))) { 213 | int num3 = HexToInt(url[i + 2]); 214 | int num4 = HexToInt(url[i + 3]); 215 | int num5 = HexToInt(url[i + 4]); 216 | int num6 = HexToInt(url[i + 5]); 217 | if (((num3 < 0) || (num4 < 0)) || ((num5 < 0) || (num6 < 0))) { 218 | goto Label_010B; 219 | } 220 | ch = (char)((((num3 << 12) | (num4 << 8)) | (num5 << 4)) | num6); 221 | i += 5; 222 | decoder.FlushBytes(false); // dont check previous stuff 223 | decoder.AddChar(ch, true); // CHECK IF CHAR OK WITH FILEPATH 224 | continue; 225 | } 226 | 227 | // ascii char (2 digit hex) 228 | int num7 = HexToInt(url[i + 1]); 229 | int num8 = HexToInt(url[i + 2]); 230 | if ((num7 >= 0) && (num8 >= 0)) { 231 | byte b = (byte)((num7 << 4) | num8); 232 | i += 2; 233 | decoder.FlushBytes(false); // dont check previous stuff 234 | decoder.AddByte(b); 235 | 236 | // check if unicode char ("%11%11") 237 | if (((i + 1) < (length - 2)) && (url[i + 1] == '%')) { 238 | 239 | // YES, unicode char 240 | num7 = HexToInt(url[i + 1]); 241 | num8 = HexToInt(url[i + 2]); 242 | if ((num7 >= 0) && (num8 >= 0)) { 243 | b = (byte)((num7 << 4) | num8); 244 | i += 2; 245 | decoder.AddByte(b); 246 | decoder.FlushBytes(true); // CHECK IF CHARS OK WITH FILEPATHS 247 | } 248 | } else { 249 | 250 | // NO, not unicode char 251 | decoder.FlushBytes(true); // CHECK IF CHARS OK WITH FILEPATHS 252 | 253 | } 254 | 255 | continue; 256 | } 257 | 258 | } 259 | 260 | Label_010B: 261 | if ((ch & 0xff80) == 0) { 262 | decoder.AddByte((byte)ch); 263 | } else { 264 | decoder.AddChar(ch, false); 265 | } 266 | } 267 | 268 | return decoder.GetString(); 269 | } 270 | 271 | /// 272 | /// UrlEncodes a string without the requirement for System.Web 273 | /// 274 | public static string EncodeURL(this string text) { 275 | return System.Uri.EscapeDataString(text); 276 | } 277 | 278 | 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/src/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache -------------------------------------------------------------------------------- /src/obj/Debug/SharpBrowser.MainForm.resources: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/src/obj/Debug/SharpBrowser.MainForm.resources -------------------------------------------------------------------------------- /src/obj/Debug/SharpBrowser.Properties.Resources.resources: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/src/obj/Debug/SharpBrowser.Properties.Resources.resources -------------------------------------------------------------------------------- /src/obj/Debug/SharpBrowser.csproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- 1 | E:\Program Files\Git\@\SharpBrowser\src\obj\Debug\SharpBrowser.csprojResolveAssemblyReference.cache 2 | E:\Program Files\Git\@\SharpBrowser\src\obj\Debug\SharpBrowser.MainForm.resources 3 | E:\Program Files\Git\@\SharpBrowser\src\obj\Debug\SharpBrowser.Properties.Resources.resources 4 | E:\Program Files\Git\@\SharpBrowser\src\obj\Debug\SharpBrowser.csproj.GenerateResource.Cache 5 | -------------------------------------------------------------------------------- /src/obj/Debug/SharpBrowser.csproj.GenerateResource.Cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/src/obj/Debug/SharpBrowser.csproj.GenerateResource.Cache -------------------------------------------------------------------------------- /src/obj/Debug/SharpBrowser.csprojResolveAssemblyReference.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/src/obj/Debug/SharpBrowser.csprojResolveAssemblyReference.cache -------------------------------------------------------------------------------- /src/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/src/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs -------------------------------------------------------------------------------- /src/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/src/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs -------------------------------------------------------------------------------- /src/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreateBrowser/SharpBrowser/bf64cb1976e39d33c8b3d5792e42197da95d459e/src/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs -------------------------------------------------------------------------------- /src/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | --------------------------------------------------------------------------------