diff --git a/.editorconfig b/.editorconfig index 47a17f8513..92683c27e2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -156,3 +156,5 @@ csharp_space_between_square_brackets = # Modifier order csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async : error +# CA2247: Argument passed to TaskCompletionSource constructor should be TaskCreationOptions enum instead of TaskContinuationOptions enum +dotnet_diagnostic.CA2247.severity = error diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index fbaecea62c..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -name: Bug report -about: Create a report for a reproducible bug -title: '' -labels: '' -assignees: '' - ---- - -### Please only open an issue if you have a BUG to report, if you simply have a question or require some assistance keep reading for info. If you do have a BUG to report, please use the Bug Report template below. - -So you have a question to ask, where can you look for answers? Read on. Think you've found a bug? Please take the time to fill out the bug report below, provide as much information as you can, make sure you provide information for every heading. Thank you! We'd like to keep issues exclusively for **bug reports**, so please ask your questions in the `Discussions` section (https://github.com/cefsharp/CefSharp/discussions) - -- Start by reading the General Usage guide, it answers all the common questions https://github.com/cefsharp/CefSharp/wiki/General-Usage -- Check out the FAQ, lots of useful information there, specially if your having trouble deploying to a different machine : https://github.com/cefsharp/CefSharp/wiki/Frequently-asked-questions -- GitHub proves a fantastic search feature, it'll search through past issues and code. So check that out (Search box at the top of this page) : https://github.com/cefsharp/CefSharp -- You can see all the `CefSharp` tagged issues on `Stackoverflow`, some useful stuff there : http://stackoverflow.com/questions/tagged/cefsharp - -Still have a question? Great, ask it on [Discussions](https://github.com/cefsharp/CefSharp/discussions) or [Stackoverflow](http://stackoverflow.com/questions/tagged/cefsharp). Larger complex questions should be asked on `Discussions` - -**Note: CefSharp is just a wrapper around the Chromium Embedded Project, it's worth searching http://magpcss.org/ceforum/index.php if your problem involves a low level Chromium error message** - -We ask that you put in a reasonable amount of effort in searching through the resources listed above. The developers have full time jobs, they have lives, families, the time they have available to contribute this project is a precious resource, make sure you use it wisely! Remember the more time we spend answering the same questions over and over again, less time goes into writing code, adding new features, actually fixing bugs! - -Still have a question to ask or unsure where to go next? Start with the Gitter Chat room : https://github.com/cefsharp/CefSharp/discussions - -Before posting a bug report please take the time to read https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/ - ---- -### Bug Report -Delete this line and everything above, and then fill in the details below. - -- **What version of the product are you using?** - - Please only create an issue if you can reproduce the problem with version 109.1.110 or greater. - - What version are you using? Nuget? CI Nuget? build from a branch? If so please link to the relevant commit. - - Please include the exact version number you are using e.g. 109.1.110 (no ambiguous statements like `Latest from Nuget`) - -- **What architecture x86 or x64?** - - -- **What version of .Net?** - <.Net 4.x/.Net Core 3.1/.Net 5.0> - -- **On what operating system?** - - -- **Are you using `WinForms`, `WPF` or `OffScreen`?** - - -- **What steps will reproduce the problem?** - - Please provide detailed information here, enough for someone else to reproduce your problem. - - Does the problem reproduce using the [MinimalExample](https://github.com/cefsharp/CefSharp.MinimalExample)? - - If code is required to reproduce your problem then please provide one of the following - - Fork the [MinimalExample](https://github.com/cefsharp/CefSharp.MinimalExample) and push your changes to `GitHub` (this is the preferred option). - - Use a code sharing service list `Gist` or `Pastebin` - - Paste your **formatted code as part of this issue** (only do this for small amounts of code and make sure you **format the code so it's readable**) - - Please no binary attachments (zip, 7z, etc), code needs to be easily reviewed in a web browser. - -- **What is the expected output? What do you see instead?** - -- **Please provide any additional information below.** - - A stack trace if available, any Exception information. - - If you are seeing a crash in `libcef.dll` then please download `libcef.dll.pdb` and place it next to `libcef.dll` to obtain a detailed stack trace, see https://github.com/cefsharp/CefSharp/wiki/Trouble-Shooting#loading-native-symbols-for-easier-diagnosis for details. - - - Does the cef log provide any relevant information? (By default there should be a debug.log file in your bin directory) - - - Any other background information that's relevant? Are you doing something out of the ordinary? 3rd party controls? - -- **Does this problem also occur in the `CEF` Sample Application** - - Download one of the following: - - For x86 download https://cef-builds.spotifycdn.com/cef_binary_109.1.11%2Bg6d4fdb2%2Bchromium-109.0.5414.87_windows32_client.tar.bz2 - - For x64 download https://cef-builds.spotifycdn.com/cef_binary_109.1.11%2Bg6d4fdb2%2Bchromium-109.0.5414.87_windows64_client.tar.bz2 - - For arm64 download https://cef-builds.spotifycdn.com/cef_binary_109.1.11%2Bg6d4fdb2%2Bchromium-109.0.5414.87_windowsarm64_client.tar.bz2 - - Extract and run cefclient.exe - - If you are using WPF/OffScreen run - ``` - cefclient.exe --multi-threaded-message-loop --no-sandbox --off-screen-rendering-enabled --enable-gpu - ``` - - If you are using WinForms run - ``` - cefclient.exe --multi-threaded-message-loop --no-sandbox - ``` - - **MAKE SURE TO TEST WITH THE COMMAND LINE ARGS LISTED ABOVE** - - If you can reproduce the problem with `cefclient` then please report the issue on https://bitbucket.org/chromiumembedded/cef/overview (Make sure you search before opening an issue). If you open an issue here it will most likely be closed as `upstream` as the bug needs to be fixed in `CEF`. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..6d110d1e67 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,155 @@ +name: Bug Report +description: Create a report for a reproducible bug +labels: [] +body: + - type: markdown + attributes: + value: | + Please only open an issue if you have a **BUG** to report. for questions/assistance use [Discussions](https://github.com/cefsharp/CefSharp/discussions). If you are new to the project then please review the following: + 1. [General Usage guide](https://github.com/cefsharp/CefSharp/wiki/General-Usage) includes examples and details of many common questions. + 2. [Frequently Asked Questions](https://github.com/cefsharp/CefSharp/wiki/Frequently-asked-questions), lots of useful information there, specially if your having trouble deploying to a different machine. + 3. GitHub has a fantastic search feature, it'll search through past issues/code. Use the Search box at the top of this page). + 4. You can see all the `CefSharp` tagged issues on [Stackoverflow](http://stackoverflow.com/questions/tagged/cefsharp), lots of questions/answers. + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched both open/closed issues, no issue already exists. + required: true + - type: input + id: cefsharp-version + attributes: + label: CefSharp Version + description: What version are you using? Please only open an issue if you can reproduce the problem with version 131.3.50 or later. + placeholder: 131.3.50 + validations: + required: true + - type: dropdown + id: operating-system + attributes: + label: Operating System + multiple: false + options: + - Windows 10 + - Windows 11 + - Windows Server 2016 + - Windows Server 2019 + - Windows Server 2022 + - Windows Server 2025 + validations: + required: true + - type: dropdown + id: architecture + attributes: + label: Architecture + multiple: false + options: + - x64 + - x86 + - arm64 + - AnyCPU + validations: + required: true + - type: input + id: dotnet-version + attributes: + label: .Net Version + description: | + What .Net version are you using? + placeholder: e.g. .Net 4.8 or .Net 6.0 + validations: + required: true + - type: dropdown + id: cefsharp-implementation + attributes: + label: Implementation + multiple: false + options: + - WinForms + - WPF + - WPF HwndHost + - OffScreen + validations: + required: true + - type: textarea + id: repro-steps + attributes: + label: Reproduction Steps + description: | + Please include minimal steps to reproduce the problem. E.g.: the smallest possible code snippet; or a small example project here on GitHub, with steps to run it. Include text/code as text rather than screenshots (so it shows up in searches and can copy/paste). + Does the problem reproduce using the [MinimalExample](https://github.com/cefsharp/CefSharp.MinimalExample)? You can fork the MinimalExample and use this as a base for your example. + placeholder: Minimal Reproduction + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: Expected behavior + description: | + Provide a description of the expected behavior. + placeholder: Expected behavior + validations: + required: true + - type: textarea + id: actual-behavior + attributes: + label: Actual behavior + description: | + Provide a description of the actual behavior observed. If applicable please include any error messages, exception or stacktraces. + placeholder: Actual behavior + validations: + required: true + - type: textarea + id: regression + attributes: + label: Regression? + description: | + Did this work in a previous build or release of CefSharp? If you can try a previous release or build to find out, that can help us narrow down the problem. If you don't know, that's OK. + placeholder: Regression? + validations: + required: false + - type: textarea + id: known-workarounds + attributes: + label: Known Workarounds + description: | + Please provide a description of any known workarounds. + placeholder: Known Workarounds + validations: + required: false + - type: markdown + attributes: + value: | + To help determine where the problem needs to be fixed please download and test using the `CEF Sample Application(cefclient)`. + 1. Download for [x86](https://cef-builds.spotifycdn.com/cef_binary_138.0.17%2Bgac9b751%2Bchromium-138.0.7204.97_windows32_client.tar.bz2) or [x64](https://cef-builds.spotifycdn.com/cef_binary_138.0.17%2Bgac9b751%2Bchromium-138.0.7204.97_windows64_client.tar.bz2) or [arm64](https://cef-builds.spotifycdn.com/cef_binary_138.0.17%2Bgac9b751%2Bchromium-138.0.7204.97_windowsarm64_client.tar.bz2). + 2. Extract tar.bz2 file + 3. Execute cefclient.exe using the **command line args below**: + + For WPF/OffScreen `cefclient.exe --multi-threaded-message-loop --no-sandbox --off-screen-rendering-enabled --enable-gpu` + For WinForms `cefclient.exe --multi-threaded-message-loop --no-sandbox --use-alloy-style` + - type: dropdown + id: cefclient-testing + attributes: + label: Does this problem also occur in the CEF Sample Application + multiple: false + options: + - 'Yes using WPF/OffScreen command line args' + - 'Yes using WinForms command line args' + - 'No' + - Not Tested + description: | + If you can reproduce the problem with `cefclient` then please report the issue on [CEF Issue Tracker](https://github.com/chromiumembedded/cef/issues) (Make sure you search before opening an issue). If you open an issue here it will most likely be closed as `upstream` as the bug needs to be fixed in `CEF`. + validations: + required: true + - type: textarea + id: other-info + attributes: + label: Other information + description: | + If you are seeing a crash in `libcef.dll` then please download `libcef.dll.pdb` and place it next to `libcef.dll` to obtain a detailed stack trace, see [FAQ](https://github.com/cefsharp/CefSharp/wiki/Trouble-Shooting#loading-native-symbols-for-easier-diagnosis) for details. + Does the cef log provide any relevant information? (By default there should be a debug.log file in your bin directory) + Any other background information that's relevant? Are you doing something out of the ordinary? 3rd party controls? + placeholder: Other information + validations: + required: false diff --git a/.vsconfig b/.vsconfig index 8dff979304..9c22b175de 100644 --- a/.vsconfig +++ b/.vsconfig @@ -10,7 +10,7 @@ "Microsoft.Net.ComponentGroup.DevelopmentPrerequisites", "Microsoft.Component.MSBuild", "Microsoft.VisualStudio.Component.ManagedDesktop.Core", - "Microsoft.Net.Component.4.5.2.TargetingPack", + "Microsoft.Net.Component.4.6.2.TargetingPack", "Microsoft.VisualStudio.Component.IntelliCode", "Microsoft.Net.ComponentGroup.TargetingPacks.Common", "Microsoft.VisualStudio.Component.Debugger.JustInTime", @@ -26,8 +26,8 @@ "Microsoft.VisualStudio.Component.VC.CLI.Support", "Microsoft.VisualStudio.Component.Windows10SDK.19041", "Microsoft.VisualStudio.Workload.NativeDesktop", - "Microsoft.NetCore.Component.DevelopmentTools", - "Microsoft.NetCore.Component.Runtime.3.1", + "Microsoft.NetCore.Component.DevelopmentTools", + "Microsoft.NetCore.Component.Runtime.6.0", "Microsoft.NetCore.Component.SDK" ] } \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1dc6fb7cb5..84a6885a92 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,15 +13,13 @@ Thanks for your interest in contributing to the project! Please follow these sim ### `CefSharp` vs `Chromium Embedded Framework(CEF)` -`CefSharp` simply takes the building blocks provided by `CEF` and attempts to provide a usable '.Net' implementation. +`CefSharp` simply takes the building blocks provided by the [Chromium Embedded Framework(CEF)](https://github.com/chromiumembedded/cef) and provides a usable '.Net' implementation. The upstream [`CEF` forum](http://magpcss.org/ceforum/) is a valuable resource, if your issues seems fairly low level, then please conduct some research before posting. It maybe helpful to run the `cefclient` application and compare output with `CefSharp`. The `WinForms` and `WPF` versions use two different rendering modes, `WPF` uses Offscreen Rendering (`OSR`). `OffScreen` also uses `OSR` mode. - - Download one of the following: - - For x86 download https://cef-builds.spotifycdn.com/cef_binary_109.1.11%2Bg6d4fdb2%2Bchromium-109.0.5414.87_windows32_client.tar.bz2 - - For x64 download https://cef-builds.spotifycdn.com/cef_binary_109.1.11%2Bg6d4fdb2%2Bchromium-109.0.5414.87_windows64_client.tar.bz2 - - For arm64 download https://cef-builds.spotifycdn.com/cef_binary_109.1.11%2Bg6d4fdb2%2Bchromium-109.0.5414.87_windowsarm64_client.tar.bz2 + - Download and run the `CEF Sample Application(cefclient)`: + - Download for [x86](https://cef-builds.spotifycdn.com/cef_binary_138.0.17%2Bgac9b751%2Bchromium-138.0.7204.97_windows32_client.tar.bz2) or [x64](https://cef-builds.spotifycdn.com/cef_binary_138.0.17%2Bgac9b751%2Bchromium-138.0.7204.97_windows64_client.tar.bz2) or [arm64](https://cef-builds.spotifycdn.com/cef_binary_138.0.17%2Bgac9b751%2Bchromium-138.0.7204.97_windowsarm64_client.tar.bz2). - Extract and run cefclient.exe - If you are using WPF/OffScreen run ``` @@ -29,10 +27,10 @@ It maybe helpful to run the `cefclient` application and compare output with `Cef ``` - If you are using WinForms run ``` - cefclient.exe --multi-threaded-message-loop --no-sandbox + cefclient.exe --multi-threaded-message-loop --no-sandbox --use-alloy-style ``` - **MAKE SURE TO TEST WITH THE COMMAND LINE ARGS LISTED ABOVE** - - If you can reproduce the problem with `cefclient` then you'll need to report the bug on https://bitbucket.org/chromiumembedded/cef/overview there is no point opening an issue here. (Make sure you search before opening an issue) + - If you can reproduce the problem with `cefclient` then you'll need to report the bug on https://github.com/chromiumembedded/cef there is no point opening an issue here. (Make sure you search before opening an issue) ### What should I include when creating an `Issue`? @@ -47,7 +45,7 @@ Your bug report should **always follow this template**: - **Are you using `WinForms`, `WPF` or `OffScreen`?** - **What version of the product are you using? On what operating system? x86 or x64?** - What version are you using? Nuget? CI Nuget? build from a branch? If so which branch? - - Win7, Win 8, Win10, etc? + - Win10/11, etc? - **Please provide any additional information below.** - A stack trace if available, any Exception information. - Does the cef log provide any relevant information? (By default there should be a debug.log file in your bin directory) diff --git a/CefSharp.AfterBuild.targets b/CefSharp.AfterBuild.targets index ef5658bd94..f26ed0b382 100644 --- a/CefSharp.AfterBuild.targets +++ b/CefSharp.AfterBuild.targets @@ -10,9 +10,9 @@ - + - + diff --git a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodHandler.h b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodHandler.h index c98a4baa29..aa315f69c9 100644 --- a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodHandler.h +++ b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodHandler.h @@ -18,11 +18,11 @@ namespace CefSharp { private: gcroot _callbackRegistry; - gcroot^> _methodCallbackSave; - int64 _objectId; + gcroot^> _methodCallbackSave; + int64_t _objectId; public: - JavascriptAsyncMethodHandler(int64 objectId, JavascriptCallbackRegistry^ callbackRegistry, Func^ methodCallbackSave) + JavascriptAsyncMethodHandler(int64_t objectId, JavascriptCallbackRegistry^ callbackRegistry, Func^ methodCallbackSave) :_callbackRegistry(callbackRegistry), _objectId(objectId), _methodCallbackSave(methodCallbackSave) { diff --git a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodWrapper.h b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodWrapper.h index 0231667583..876aa52026 100644 --- a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodWrapper.h @@ -19,7 +19,7 @@ namespace CefSharp MCefRefPtr _javascriptMethodHandler; public: - JavascriptAsyncMethodWrapper(int64 ownerId, JavascriptCallbackRegistry^ callbackRegistry, Func^ methodCallbackSave) + JavascriptAsyncMethodWrapper(int64_t ownerId, JavascriptCallbackRegistry^ callbackRegistry, Func^ methodCallbackSave) : _javascriptMethodHandler(new JavascriptAsyncMethodHandler(ownerId, callbackRegistry, methodCallbackSave)) { diff --git a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncObjectWrapper.h b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncObjectWrapper.h index 6aa5d00d1b..f42ac60e42 100644 --- a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncObjectWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncObjectWrapper.h @@ -18,11 +18,11 @@ namespace CefSharp { private: initonly List^ _wrappedMethods; - Func^ _methodCallbackSave; + Func^ _methodCallbackSave; JavascriptCallbackRegistry^ _callbackRegistry; public: - JavascriptAsyncObjectWrapper(JavascriptCallbackRegistry^ callbackRegistry, Func^ saveMethod) + JavascriptAsyncObjectWrapper(JavascriptCallbackRegistry^ callbackRegistry, Func^ saveMethod) : _wrappedMethods(gcnew List()), _methodCallbackSave(saveMethod), _callbackRegistry(callbackRegistry) { diff --git a/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h b/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h index bbe39fe54b..9ece880b2b 100644 --- a/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h +++ b/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h @@ -151,14 +151,14 @@ namespace CefSharp auto rootObjectWrappers = _browserWrapper->JavascriptRootObjectWrappers; JavascriptRootObjectWrapper^ rootObject; - if (!rootObjectWrappers->TryGetValue(frame->GetIdentifier(), rootObject)) + if (!rootObjectWrappers->TryGetValue(StringUtils::ToClr(frame->GetIdentifier()), rootObject)) { #ifdef NETCOREAPP rootObject = gcnew JavascriptRootObjectWrapper(browser->GetIdentifier()); #else rootObject = gcnew JavascriptRootObjectWrapper(browser->GetIdentifier(), _browserWrapper->BrowserProcess); #endif - rootObjectWrappers->TryAdd(frame->GetIdentifier(), rootObject); + rootObjectWrappers->TryAdd(StringUtils::ToClr(frame->GetIdentifier()), rootObject); } //Cached objects only contains a list of objects not already bound diff --git a/CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h b/CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h index 0587553c00..f4f9ca45be 100644 --- a/CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h +++ b/CefSharp.BrowserSubprocess.Core/BrowserSubprocessExecutable.h @@ -28,13 +28,12 @@ namespace CefSharp } +#ifdef NETCOREAPP /// /// This function should be called from the application entry point function (typically Program.Main) /// to execute a secondary process e.g. gpu, renderer, utility /// This overload is specifically used for .Net Core. For hosting your own BrowserSubProcess /// it's preferable to use the Main method provided by this class. - /// - Obtains the command line args via a call to Environment::GetCommandLineArgs - /// - Calls CefEnableHighDPISupport before any other processing /// /// /// If called for the browser process (identified by no "type" command-line value) it will return immediately @@ -46,6 +45,7 @@ namespace CefSharp auto subProcess = gcnew BrowserSubprocessExecutable(); return subProcess->Main(args, nullptr); } +#endif /// /// This function should be called from the application entry point function (typically Program.Main) @@ -53,7 +53,6 @@ namespace CefSharp /// This overload is specifically used for .Net Core. For hosting your own BrowserSubProcess /// it's preferable to use the Main method provided by this class. /// - Obtains the command line args via a call to Environment::GetCommandLineArgs - /// - Calls CefEnableHighDPISupport before any other processing /// /// /// If called for the browser process (identified by no "type" command-line value) it will return immediately @@ -62,8 +61,6 @@ namespace CefSharp /// TryAdd(browser->GetIdentifier(), wrapper); + if (!extraInfo.get()) + { + return; + } + //For the main browser only we check LegacyBindingEnabled and //load the objects. Popups don't send this information and checking //will override the _legacyBindingEnabled field @@ -126,6 +131,12 @@ namespace CefSharp _handler->OnContextCreated(% browserWrapper, % frameWrapper, % contextWrapper); } + //Skip additional contexts (DevTools, extensions) to avoid + //double binding and duplicate calls to IRenderProcessMessageHandler.OnContextCreated + //https://github.com/chromiumembedded/cef/issues/3867 + bool isSameContext = frame->GetV8Context()->IsSame(context); + if (!isSameContext) + return; auto rootObject = GetJsRootObjectWrapper(browser->GetIdentifier(), frame->GetIdentifier()); if (_legacyBindingEnabled) @@ -220,7 +231,7 @@ namespace CefSharp auto rootObjectWrappers = browserWrapper->JavascriptRootObjectWrappers; JavascriptRootObjectWrapper^ wrapper; - if (rootObjectWrappers->TryRemove(frame->GetIdentifier(), wrapper)) + if (rootObjectWrappers->TryRemove(StringUtils::ToClr(frame->GetIdentifier()), wrapper)) { delete wrapper; } @@ -292,7 +303,7 @@ namespace CefSharp frame->SendProcessMessage(CefProcessId::PID_BROWSER, uncaughtExceptionMessage); } - JavascriptRootObjectWrapper^ CefAppUnmanagedWrapper::GetJsRootObjectWrapper(int browserId, int64 frameId) + JavascriptRootObjectWrapper^ CefAppUnmanagedWrapper::GetJsRootObjectWrapper(int browserId, CefString& frameId) { auto browserWrapper = FindBrowserWrapper(browserId); @@ -302,16 +313,17 @@ namespace CefSharp } auto rootObjectWrappers = browserWrapper->JavascriptRootObjectWrappers; + auto frameIdClr = StringUtils::ToClr(frameId); JavascriptRootObjectWrapper^ rootObject; - if (!rootObjectWrappers->TryGetValue(frameId, rootObject)) + if (!rootObjectWrappers->TryGetValue(frameIdClr, rootObject)) { #ifdef NETCOREAPP rootObject = gcnew JavascriptRootObjectWrapper(browserId); #else rootObject = gcnew JavascriptRootObjectWrapper(browserId, browserWrapper->BrowserProcess); #endif - rootObjectWrappers->TryAdd(frameId, rootObject); + rootObjectWrappers->TryAdd(frameIdClr, rootObject); } return rootObject; @@ -400,8 +412,8 @@ namespace CefSharp } //both messages have callbackId stored at index 0 - auto frameId = frame->GetIdentifier(); - int64 callbackId = GetInt64(argList, 0); + auto frameId = StringUtils::ToClr(frame->GetIdentifier()); + int64_t callbackId = GetInt64(argList, 0); if (name == kEvaluateJavascriptRequest) { @@ -604,7 +616,7 @@ namespace CefSharp { auto jsCallbackId = GetInt64(argList, 0); JavascriptRootObjectWrapper^ rootObjectWrapper; - browserWrapper->JavascriptRootObjectWrappers->TryGetValue(frame->GetIdentifier(), rootObjectWrapper); + browserWrapper->JavascriptRootObjectWrappers->TryGetValue(StringUtils::ToClr(frame->GetIdentifier()), rootObjectWrapper); if (rootObjectWrapper != nullptr && rootObjectWrapper->CallbackRegistry != nullptr) { rootObjectWrapper->CallbackRegistry->Deregister(jsCallbackId); @@ -712,7 +724,7 @@ namespace CefSharp { if (frame.get() && frame->IsValid()) { - auto frameId = frame->GetIdentifier(); + auto frameId = StringUtils::ToClr(frame->GetIdentifier()); auto callbackId = GetInt64(argList, 0); JavascriptRootObjectWrapper^ rootObjectWrapper; diff --git a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h index b202f31aa8..38f8da2881 100644 --- a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h @@ -72,7 +72,7 @@ namespace CefSharp } CefBrowserWrapper^ FindBrowserWrapper(int browserId); - JavascriptRootObjectWrapper^ GetJsRootObjectWrapper(int browserId, int64 frameId); + JavascriptRootObjectWrapper^ GetJsRootObjectWrapper(int browserId, CefString& frameId); virtual DECL CefRefPtr GetRenderProcessHandler() override; virtual DECL void OnBrowserCreated(CefRefPtr browser, CefRefPtr extraInfo) override; diff --git a/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h b/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h index 1e32ecea5e..ef9ef6dfb5 100644 --- a/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/CefBrowserWrapper.h @@ -31,7 +31,7 @@ namespace CefSharp internal: //Frame Identifier is used as Key - property ConcurrentDictionary^ JavascriptRootObjectWrappers; + property ConcurrentDictionary^ JavascriptRootObjectWrappers; public: CefBrowserWrapper(CefRefPtr cefBrowser) @@ -40,7 +40,7 @@ namespace CefSharp BrowserId = cefBrowser->GetIdentifier(); IsPopup = cefBrowser->IsPopup(); - JavascriptRootObjectWrappers = gcnew ConcurrentDictionary(); + JavascriptRootObjectWrappers = gcnew ConcurrentDictionary(); } !CefBrowserWrapper() @@ -54,7 +54,7 @@ namespace CefSharp if (JavascriptRootObjectWrappers != nullptr) { - for each (KeyValuePair entry in JavascriptRootObjectWrappers) + for each (KeyValuePair entry in JavascriptRootObjectWrappers) { delete entry.Value; } diff --git a/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.netcore.vcxproj b/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.netcore.vcxproj index 55d33cb146..7cf73b78a6 100644 --- a/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.netcore.vcxproj +++ b/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.netcore.vcxproj @@ -1,6 +1,6 @@  - + @@ -36,8 +36,9 @@ CefSharp.BrowserSubprocess.Core CefSharpBrowserSubprocessCore NetCoreCProj - netcoreapp3.1 + net6.0 10.0 + 10.0.10240.0 @@ -153,10 +154,11 @@ Use true true + stdcpp17 true - libcef.lib;libcef_dll_wrapper.lib + libcef.lib;libcef_dll_wrapper.lib;User32.lib $(SolutionDir)packages\$(CefSdkVer)\CEF\$(Platform)\$(Configuration);$(SolutionDir)packages\$(CefSdkVer)\CEF\$(Platform)\$(Configuration)\VS$(VisualStudioProductVersion); /ignore:4099 %(AdditionalOptions) @@ -172,10 +174,11 @@ _DEBUG;EXPORT;NETCOREAPP;%(PreprocessorDefinitions) Use true + stdcpp17 true - libcef.lib;libcef_dll_wrapper.lib + libcef.lib;libcef_dll_wrapper.lib;User32.lib $(SolutionDir)packages\$(CefSdkVer)\CEF\$(Platform)\$(Configuration);$(SolutionDir)packages\$(CefSdkVer)\CEF\$(Platform)\$(Configuration)\VS$(VisualStudioProductVersion); /ignore:4099 %(AdditionalOptions) @@ -191,10 +194,11 @@ _DEBUG;EXPORT;NETCOREAPP;%(PreprocessorDefinitions) Use true + stdcpp17 true - libcef.lib;libcef_dll_wrapper.lib + libcef.lib;libcef_dll_wrapper.lib;User32.lib $(SolutionDir)packages\$(CefSdkVer)\CEF\$(Platform)\$(Configuration);$(SolutionDir)packages\$(CefSdkVer)\CEF\$(Platform)\$(Configuration)\VS$(VisualStudioProductVersion); /ignore:4099 %(AdditionalOptions) @@ -210,6 +214,7 @@ Use true true + stdcpp17 true @@ -227,6 +232,7 @@ NDEBUG;EXPORT;NETCOREAPP;%(PreprocessorDefinitions) Use true + stdcpp17 true @@ -244,6 +250,7 @@ NDEBUG;EXPORT;NETCOREAPP;%(PreprocessorDefinitions) Use true + stdcpp17 true diff --git a/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj b/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj index cc9ac89021..f7cc5096d6 100644 --- a/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj +++ b/CefSharp.BrowserSubprocess.Core/CefSharp.BrowserSubprocess.Core.vcxproj @@ -1,6 +1,6 @@  - + @@ -26,7 +26,7 @@ {6C4BB501-2F8E-48AC-9AB5-8CFB2D74185C} ManagedCProj CefSharpBrowserSubprocessCore - v4.5.2 + v4.6.2 10.0 @@ -111,10 +111,11 @@ true true true + stdcpp17 true - libcef.lib;libcef_dll_wrapper.lib + libcef.lib;libcef_dll_wrapper.lib;User32.lib $(SolutionDir)packages\$(CefSdkVer)\CEF\$(Platform)\$(Configuration);$(SolutionDir)packages\$(CefSdkVer)\CEF\$(Platform)\$(Configuration)\VS$(VisualStudioProductVersion); /ignore:4099 %(AdditionalOptions) @@ -128,10 +129,11 @@ Use true true + stdcpp17 true - libcef.lib;libcef_dll_wrapper.lib + libcef.lib;libcef_dll_wrapper.lib;User32.lib $(SolutionDir)packages\$(CefSdkVer)\CEF\$(Platform)\$(Configuration);$(SolutionDir)packages\$(CefSdkVer)\CEF\$(Platform)\$(Configuration)\VS$(VisualStudioProductVersion); /ignore:4099 %(AdditionalOptions) @@ -144,6 +146,7 @@ Use true true + stdcpp17 true @@ -158,6 +161,7 @@ WIN32;NDEBUG;EXPORT;%(PreprocessorDefinitions) Use true + stdcpp17 true diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.cpp b/CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.cpp index 0cea75ec00..01f6da2326 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.cpp +++ b/CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.cpp @@ -21,11 +21,11 @@ namespace CefSharp auto result = gcnew JavascriptCallback(); result->Id = newId; result->BrowserId = _browserId; - result->FrameId = context->GetFrame()->GetIdentifier(); + result->FrameId = StringUtils::ToClr(context->GetFrame()->GetIdentifier()); return result; } - JavascriptCallbackWrapper^ JavascriptCallbackRegistry::FindWrapper(int64 id) + JavascriptCallbackWrapper^ JavascriptCallbackRegistry::FindWrapper(int64_t id) { JavascriptCallbackWrapper^ callback; _callbacks->TryGetValue(id, callback); diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.h b/CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.h index 46db9b23b3..dd0af41f40 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.h +++ b/CefSharp.BrowserSubprocess.Core/JavascriptCallbackRegistry.h @@ -24,7 +24,7 @@ namespace CefSharp ConcurrentDictionary^ _callbacks; internal: - JavascriptCallbackWrapper^ FindWrapper(int64 id); + JavascriptCallbackWrapper^ FindWrapper(int64_t id); public: JavascriptCallbackRegistry(int browserId) : _browserId(browserId) diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptMethodWrapper.h b/CefSharp.BrowserSubprocess.Core/JavascriptMethodWrapper.h index 3f402f5f59..ebf980b8cc 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptMethodWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/JavascriptMethodWrapper.h @@ -20,12 +20,12 @@ namespace CefSharp { private: MCefRefPtr _javascriptMethodHandler; - int64 _ownerId; + int64_t _ownerId; String^ _javascriptMethodName; IBrowserProcess^ _browserProcess; public: - JavascriptMethodWrapper(int64 ownerId, IBrowserProcess^ browserProcess, JavascriptCallbackRegistry^ callbackRegistry) + JavascriptMethodWrapper(int64_t ownerId, IBrowserProcess^ browserProcess, JavascriptCallbackRegistry^ callbackRegistry) { _ownerId = ownerId; _browserProcess = browserProcess; diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptObjectWrapper.h b/CefSharp.BrowserSubprocess.Core/JavascriptObjectWrapper.h index 785df10d96..28eebd3b55 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptObjectWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/JavascriptObjectWrapper.h @@ -26,7 +26,7 @@ namespace CefSharp List^ _wrappedProperties; IBrowserProcess^ _browserProcess; MCefRefPtr _jsPropertyHandler; - int64 _objectId; + int64_t _objectId; public: JavascriptObjectWrapper(IBrowserProcess^ browserProcess) diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptPromiseResolverCatch.h b/CefSharp.BrowserSubprocess.Core/JavascriptPromiseResolverCatch.h index c3dbddac2c..68fccd20e0 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptPromiseResolverCatch.h +++ b/CefSharp.BrowserSubprocess.Core/JavascriptPromiseResolverCatch.h @@ -14,11 +14,11 @@ namespace CefSharp { private class JavascriptPromiseResolverCatch : public CefV8Handler { - int64 _callbackId; + int64_t _callbackId; bool _isJsCallback; public: - JavascriptPromiseResolverCatch(int64 callbackId, bool isJsCallback) : _callbackId(callbackId), _isJsCallback(isJsCallback) + JavascriptPromiseResolverCatch(int64_t callbackId, bool isJsCallback) : _callbackId(callbackId), _isJsCallback(isJsCallback) { } diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptPromiseResolverThen.h b/CefSharp.BrowserSubprocess.Core/JavascriptPromiseResolverThen.h index e2fa33721f..49bc984129 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptPromiseResolverThen.h +++ b/CefSharp.BrowserSubprocess.Core/JavascriptPromiseResolverThen.h @@ -19,11 +19,11 @@ namespace CefSharp { private class JavascriptPromiseResolverThen : public CefV8Handler { - int64 _callbackId; + int64_t _callbackId; bool _isJsCallback; public: - JavascriptPromiseResolverThen(int64 callbackId, bool isJsCallback) : _callbackId(callbackId), _isJsCallback(isJsCallback) + JavascriptPromiseResolverThen(int64_t callbackId, bool isJsCallback) : _callbackId(callbackId), _isJsCallback(isJsCallback) { } diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptPropertyWrapper.cpp b/CefSharp.BrowserSubprocess.Core/JavascriptPropertyWrapper.cpp index 0325cd3688..63ec03618d 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptPropertyWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/JavascriptPropertyWrapper.cpp @@ -31,7 +31,7 @@ namespace CefSharp { auto propertyAttribute = javascriptProperty->IsReadOnly ? V8_PROPERTY_ATTRIBUTE_READONLY : V8_PROPERTY_ATTRIBUTE_NONE; - v8Value->SetValue(propertyName, V8_ACCESS_CONTROL_DEFAULT, propertyAttribute); + v8Value->SetValue(propertyName, propertyAttribute); } }; } diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptPropertyWrapper.h b/CefSharp.BrowserSubprocess.Core/JavascriptPropertyWrapper.h index 7b3abdaa1e..d0ac14ae18 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptPropertyWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/JavascriptPropertyWrapper.h @@ -17,13 +17,13 @@ namespace CefSharp private ref class JavascriptPropertyWrapper { private: - int64 _ownerId; + int64_t _ownerId; IBrowserProcess^ _browserProcess; //TODO: Strongly type this variable - currently trying to include JavascriptObjectWrapper.h creates a circular reference, so won't compile Object^ _javascriptObjectWrapper; public: - JavascriptPropertyWrapper(int64 ownerId, IBrowserProcess^ browserProcess) + JavascriptPropertyWrapper(int64_t ownerId, IBrowserProcess^ browserProcess) { _ownerId = ownerId; _browserProcess = browserProcess; diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.cpp b/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.cpp index af73c6e896..6e49c59df4 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.cpp @@ -18,7 +18,7 @@ namespace CefSharp { if (objects->Count > 0) { - auto saveMethod = gcnew Func(this, &JavascriptRootObjectWrapper::SaveMethodCallback); + auto saveMethod = gcnew Func(this, &JavascriptRootObjectWrapper::SaveMethodCallback); for each (JavascriptObject ^ obj in Enumerable::OfType(objects)) { @@ -54,14 +54,14 @@ namespace CefSharp return _callbackRegistry; } - int64 JavascriptRootObjectWrapper::SaveMethodCallback(JavascriptAsyncMethodCallback^ callback) + int64_t JavascriptRootObjectWrapper::SaveMethodCallback(JavascriptAsyncMethodCallback^ callback) { auto callbackId = Interlocked::Increment(_lastCallback); _methodCallbacks->Add(callbackId, callback); return callbackId; } - bool JavascriptRootObjectWrapper::TryGetAndRemoveMethodCallback(int64 id, JavascriptAsyncMethodCallback^% callback) + bool JavascriptRootObjectWrapper::TryGetAndRemoveMethodCallback(int64_t id, JavascriptAsyncMethodCallback^% callback) { bool result = false; if (result = _methodCallbacks->TryGetValue(id, callback)) diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h b/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h index ca2879b242..45b94b8ed4 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h @@ -33,13 +33,13 @@ namespace CefSharp private: //Only access through Interlocked::Increment - used to generate unique callback Id's //Is static so ids are unique to this process https://github.com/cefsharp/CefSharp/issues/2792 - static int64 _lastCallback; + static int64_t _lastCallback; #ifndef NETCOREAPP initonly List^ _wrappedObjects; #endif initonly List^ _wrappedAsyncObjects; - initonly Dictionary^ _methodCallbacks; + initonly Dictionary^ _methodCallbacks; #ifndef NETCOREAPP IBrowserProcess^ _browserProcess; #endif @@ -47,7 +47,7 @@ namespace CefSharp // call directly into. JavascriptCallbackRegistry^ _callbackRegistry; - int64 SaveMethodCallback(JavascriptAsyncMethodCallback^ callback); + int64_t SaveMethodCallback(JavascriptAsyncMethodCallback^ callback); internal: property JavascriptCallbackRegistry^ CallbackRegistry @@ -68,7 +68,7 @@ namespace CefSharp #endif _wrappedAsyncObjects = gcnew List(); _callbackRegistry = gcnew JavascriptCallbackRegistry(browserId); - _methodCallbacks = gcnew Dictionary(); + _methodCallbacks = gcnew Dictionary(); } ~JavascriptRootObjectWrapper() @@ -101,7 +101,7 @@ namespace CefSharp _methodCallbacks->Clear(); } - bool TryGetAndRemoveMethodCallback(int64 id, JavascriptAsyncMethodCallback^% callback); + bool TryGetAndRemoveMethodCallback(int64_t id, JavascriptAsyncMethodCallback^% callback); void Bind(ICollection^ objects, const CefRefPtr& v8Value); }; diff --git a/CefSharp.BrowserSubprocess.Core/RegisterBoundObjectRegistry.h b/CefSharp.BrowserSubprocess.Core/RegisterBoundObjectRegistry.h index c9602da479..7dd5aaae47 100644 --- a/CefSharp.BrowserSubprocess.Core/RegisterBoundObjectRegistry.h +++ b/CefSharp.BrowserSubprocess.Core/RegisterBoundObjectRegistry.h @@ -24,14 +24,14 @@ namespace CefSharp private: //Only access through Interlocked::Increment - used to generate unique callback Id's //Is static so ids are unique to this process https://github.com/cefsharp/CefSharp/issues/2792 - static int64 _lastCallback; + static int64_t _lastCallback; - initonly Dictionary^ _methodCallbacks; + initonly Dictionary^ _methodCallbacks; public: RegisterBoundObjectRegistry() { - _methodCallbacks = gcnew Dictionary(); + _methodCallbacks = gcnew Dictionary(); } ~RegisterBoundObjectRegistry() @@ -43,14 +43,14 @@ namespace CefSharp _methodCallbacks->Clear(); } - int64 SaveMethodCallback(JavascriptAsyncMethodCallback^ callback) + int64_t SaveMethodCallback(JavascriptAsyncMethodCallback^ callback) { auto callbackId = Interlocked::Increment(_lastCallback); _methodCallbacks->Add(callbackId, callback); return callbackId; } - bool TryGetAndRemoveMethodCallback(int64 id, JavascriptAsyncMethodCallback^% callback) + bool TryGetAndRemoveMethodCallback(int64_t id, JavascriptAsyncMethodCallback^% callback) { bool result = false; if (result = _methodCallbacks->TryGetValue(id, callback)) diff --git a/CefSharp.BrowserSubprocess.Core/Resource.rc b/CefSharp.BrowserSubprocess.Core/Resource.rc index 2358f9a65e..f964fd970d 100644 --- a/CefSharp.BrowserSubprocess.Core/Resource.rc +++ b/CefSharp.BrowserSubprocess.Core/Resource.rc @@ -1,8 +1,8 @@ #pragma code_page(65001) 1 VERSIONINFO - FILEVERSION 110,0,220 - PRODUCTVERSION 110,0,220 + FILEVERSION 138,0,330 + PRODUCTVERSION 138,0,330 FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L @@ -18,10 +18,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "CefSharp.BrowserSubprocess.Core" - VALUE "FileVersion", "110.0.220" + VALUE "FileVersion", "138.0.330" VALUE "LegalCopyright", "Copyright © 2023 The CefSharp Authors" VALUE "ProductName", "CefSharp" - VALUE "ProductVersion", "110.0.220" + VALUE "ProductVersion", "138.0.330" END END BLOCK "VarFileInfo" diff --git a/CefSharp.BrowserSubprocess.Core/Serialization/V8Serialization.cpp b/CefSharp.BrowserSubprocess.Core/Serialization/V8Serialization.cpp index 19cfa3578d..307ef0b0d4 100644 --- a/CefSharp.BrowserSubprocess.Core/Serialization/V8Serialization.cpp +++ b/CefSharp.BrowserSubprocess.Core/Serialization/V8Serialization.cpp @@ -71,6 +71,10 @@ namespace CefSharp { SetCefTime(list, index, obj->GetDateValue().val); } + else if (obj->IsArrayBuffer()) + { + SetArrayBuffer(list, index, obj->GetArrayBufferByteLength(), obj->GetArrayBufferData()); + } else if (obj->IsArray()) { int arrLength = obj->GetArrayLength(); diff --git a/CefSharp.BrowserSubprocess.Core/SubProcess.h b/CefSharp.BrowserSubprocess.Core/SubProcess.h index 01416c1843..8f988f561b 100644 --- a/CefSharp.BrowserSubprocess.Core/SubProcess.h +++ b/CefSharp.BrowserSubprocess.Core/SubProcess.h @@ -67,11 +67,6 @@ namespace CefSharp } - static void EnableHighDPISupport() - { - CefEnableHighDPISupport(); - } - static int ExecuteProcess(IEnumerable^ args) { auto hInstance = Process::GetCurrentProcess()->Handle; diff --git a/CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h b/CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h index 761e5344f1..18291ddd44 100644 --- a/CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h +++ b/CefSharp.BrowserSubprocess.Core/WcfBrowserSubprocessExecutable.h @@ -31,6 +31,24 @@ namespace CefSharp { } + + /// + /// This function should be called from the application entry point function (typically Program.Main) + /// to execute a secondary process e.g. gpu, renderer, utility + /// This overload is specifically used for .Net 4.x. For hosting your own BrowserSubProcess + /// it's preferable to use the Main method provided by this class. + /// + /// + /// If called for the browser process (identified by no "type" command-line value) it will return immediately + /// with a value of -1. If called for a recognized secondary process it will block until the process should exit + /// and then return the process exit code. + /// ^ args) + { + auto subProcess = gcnew WcfBrowserSubprocessExecutable(); + return subProcess->Main(args, nullptr); + } + protected: SubProcess^ GetSubprocess(IEnumerable^ args, int parentProcessId, IRenderProcessHandler^ handler) override { diff --git a/CefSharp.BrowserSubprocess.Core/Wrapper/Browser.cpp b/CefSharp.BrowserSubprocess.Core/Wrapper/Browser.cpp index 3f1f9852c7..4e07c1adf4 100644 --- a/CefSharp.BrowserSubprocess.Core/Wrapper/Browser.cpp +++ b/CefSharp.BrowserSubprocess.Core/Wrapper/Browser.cpp @@ -155,9 +155,9 @@ IFrame^ Browser::FocusedFrame::get() // Returns the frame with the specified identifier, or NULL if not found. /// /*--cef(capi_name=get_frame_byident)--*/ -IFrame^ Browser::GetFrame(Int64 identifier) +IFrame^ Browser::GetFrameByIdentifier(String^ identifier) { - auto frame = _browser->GetFrame(identifier); + auto frame = _browser->GetFrameByIdentifier(StringUtils::ToNative(identifier)); if (frame.get()) { @@ -171,9 +171,9 @@ IFrame^ Browser::GetFrame(Int64 identifier) // Returns the frame with the specified name, or NULL if not found. /// /*--cef(optional_param=name)--*/ -IFrame^ Browser::GetFrame(String^ name) +IFrame^ Browser::GetFrameByName(String^ name) { - auto frame = _browser->GetFrame(StringUtils::ToNative(name)); + auto frame = _browser->GetFrameByName(StringUtils::ToNative(name)); if (frame.get()) { @@ -196,14 +196,14 @@ int Browser::GetFrameCount() // Returns the identifiers of all existing frames. /// /*--cef(count_func=identifiers:GetFrameCount)--*/ -List^ Browser::GetFrameIdentifiers() +List^ Browser::GetFrameIdentifiers() { - std::vector identifiers; + std::vector identifiers; _browser->GetFrameIdentifiers(identifiers); - List^ results = gcnew List(static_cast(identifiers.size())); + List^ results = gcnew List(static_cast(identifiers.size())); for (UINT i = 0; i < identifiers.size(); i++) { - results->Add(identifiers[i]); + results->Add(StringUtils::ToClr(identifiers[i])); } return results; } @@ -220,6 +220,24 @@ List^ Browser::GetFrameNames() return StringUtils::ToClr(names); } +IReadOnlyCollection^ Browser::GetAllFrames() +{ + std::vector identifiers; + _browser->GetFrameIdentifiers(identifiers); + + auto results = gcnew List(static_cast(identifiers.size())); + for (UINT i = 0; i < identifiers.size(); i++) + { + auto frame = _browser->GetFrameByIdentifier(identifiers[i]); + + if (frame.get()) + { + results->Add(gcnew Frame(frame)); + } + } + return results; +} + bool Browser::IsDisposed::get() { return _disposed; diff --git a/CefSharp.BrowserSubprocess.Core/Wrapper/Browser.h b/CefSharp.BrowserSubprocess.Core/Wrapper/Browser.h index c58e23f119..a81f3d1359 100644 --- a/CefSharp.BrowserSubprocess.Core/Wrapper/Browser.h +++ b/CefSharp.BrowserSubprocess.Core/Wrapper/Browser.h @@ -158,13 +158,13 @@ namespace CefSharp // Returns the frame with the specified identifier, or NULL if not found. /// /*--cef(capi_name=get_frame_byident)--*/ - virtual IFrame^ GetFrame(Int64 identifier); + virtual IFrame^ GetFrameByIdentifier(String^ identifier); /// // Returns the frame with the specified name, or NULL if not found. /// /*--cef(optional_param=name)--*/ - virtual IFrame^ GetFrame(String^ name); + virtual IFrame^ GetFrameByName(String^ name); /// // Returns the number of frames that currently exist. @@ -176,7 +176,7 @@ namespace CefSharp // Returns the identifiers of all existing frames. /// /*--cef(count_func=identifiers:GetFrameCount)--*/ - virtual List^ GetFrameIdentifiers(); + virtual List^ GetFrameIdentifiers(); /// // Returns the names of all existing frames. @@ -184,6 +184,8 @@ namespace CefSharp /*--cef()--*/ virtual List^ GetFrameNames(); + virtual IReadOnlyCollection^ GetAllFrames(); + virtual property bool IsDisposed { bool get(); diff --git a/CefSharp.BrowserSubprocess.Core/Wrapper/Frame.cpp b/CefSharp.BrowserSubprocess.Core/Wrapper/Frame.cpp index 09bfb02e4d..c5b1b3c50e 100644 --- a/CefSharp.BrowserSubprocess.Core/Wrapper/Frame.cpp +++ b/CefSharp.BrowserSubprocess.Core/Wrapper/Frame.cpp @@ -205,9 +205,9 @@ String^ Frame::Name::get() // Returns the globally unique identifier for this frame. /// /*--cef()--*/ -Int64 Frame::Identifier::get() +String^ Frame::Identifier::get() { - return _frame->GetIdentifier(); + return StringUtils::ToClr(_frame->GetIdentifier()); } /// diff --git a/CefSharp.BrowserSubprocess.Core/Wrapper/Frame.h b/CefSharp.BrowserSubprocess.Core/Wrapper/Frame.h index bae52645ae..b38b767615 100644 --- a/CefSharp.BrowserSubprocess.Core/Wrapper/Frame.h +++ b/CefSharp.BrowserSubprocess.Core/Wrapper/Frame.h @@ -201,9 +201,9 @@ namespace CefSharp // Returns the globally unique identifier for this frame. /// /*--cef()--*/ - virtual property Int64 Identifier + virtual property String^ Identifier { - Int64 get(); + String^ get(); } /// diff --git a/CefSharp.BrowserSubprocess.Core/packages.CefSharp.BrowserSubprocess.Core.config b/CefSharp.BrowserSubprocess.Core/packages.CefSharp.BrowserSubprocess.Core.config index 8a074ae34f..8b63e8ee8f 100644 --- a/CefSharp.BrowserSubprocess.Core/packages.CefSharp.BrowserSubprocess.Core.config +++ b/CefSharp.BrowserSubprocess.Core/packages.CefSharp.BrowserSubprocess.Core.config @@ -1,6 +1,6 @@  - + diff --git a/CefSharp.BrowserSubprocess.Core/packages.CefSharp.BrowserSubprocess.Core.netcore.config b/CefSharp.BrowserSubprocess.Core/packages.CefSharp.BrowserSubprocess.Core.netcore.config index 0aa3345956..22824eb76f 100644 --- a/CefSharp.BrowserSubprocess.Core/packages.CefSharp.BrowserSubprocess.Core.netcore.config +++ b/CefSharp.BrowserSubprocess.Core/packages.CefSharp.BrowserSubprocess.Core.netcore.config @@ -1,6 +1,6 @@  - + diff --git a/CefSharp.BrowserSubprocess/CefSharp.BrowserSubprocess.csproj b/CefSharp.BrowserSubprocess/CefSharp.BrowserSubprocess.csproj index 30ba63279c..3641cb9b33 100644 --- a/CefSharp.BrowserSubprocess/CefSharp.BrowserSubprocess.csproj +++ b/CefSharp.BrowserSubprocess/CefSharp.BrowserSubprocess.csproj @@ -1,6 +1,6 @@ - net452 + net462 WinExe x86;x64 false @@ -9,7 +9,7 @@ ..\CefSharp.snk app.manifest CefSharp.BrowserSubprocess.Program - win7-x86;win7-x64 + win-x86;win-x64 @@ -34,6 +34,12 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/CefSharp.BrowserSubprocess/CefSharp.BrowserSubprocess.netcore.csproj b/CefSharp.BrowserSubprocess/CefSharp.BrowserSubprocess.netcore.csproj index 19c72e7989..abce3a7a89 100644 --- a/CefSharp.BrowserSubprocess/CefSharp.BrowserSubprocess.netcore.csproj +++ b/CefSharp.BrowserSubprocess/CefSharp.BrowserSubprocess.netcore.csproj @@ -12,7 +12,7 @@ WinExe - netcoreapp3.1;net5.0 + net6.0-windows CefSharp.BrowserSubprocess CefSharp.BrowserSubprocess false @@ -25,7 +25,7 @@ arm64 Major @@ -51,6 +51,12 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/CefSharp.BrowserSubprocess/Program.cs b/CefSharp.BrowserSubprocess/Program.cs index d0ae6a14ff..6c6a75457d 100644 --- a/CefSharp.BrowserSubprocess/Program.cs +++ b/CefSharp.BrowserSubprocess/Program.cs @@ -20,8 +20,6 @@ public static int Main(string[] args) { Debug.WriteLine("BrowserSubprocess starting up with command line: " + string.Join("\n", args)); - SubProcess.EnableHighDPISupport(); - //Add your own custom implementation of IRenderProcessHandler here IRenderProcessHandler handler = null; diff --git a/CefSharp.BrowserSubprocess/Program.netcore.cs b/CefSharp.BrowserSubprocess/Program.netcore.cs index e021a07258..b29565f306 100644 --- a/CefSharp.BrowserSubprocess/Program.netcore.cs +++ b/CefSharp.BrowserSubprocess/Program.netcore.cs @@ -9,7 +9,7 @@ namespace CefSharp.BrowserSubprocess { /// /// When implementing your own BrowserSubprocess - /// - For .Net Core use (No WCF Support) + /// - For .Net 6+ use (No WCF Support) /// - Include an app.manifest with the dpi/compatability sections, this is required (this project contains the relevant). /// - If you are targeting x86/Win32 then you should set /LargeAddressAware (https://docs.microsoft.com/en-us/cpp/build/reference/largeaddressaware?view=vs-2017) /// @@ -19,8 +19,6 @@ public static int Main(string[] args) { Debug.WriteLine("BrowserSubprocess starting up with command line: " + string.Join("\n", args)); - SubProcess.EnableHighDPISupport(); - //Add your own custom implementation of IRenderProcessHandler here IRenderProcessHandler handler = null; diff --git a/CefSharp.BrowserSubprocess/app.config b/CefSharp.BrowserSubprocess/app.config index de82893200..ac5aa757cc 100644 --- a/CefSharp.BrowserSubprocess/app.config +++ b/CefSharp.BrowserSubprocess/app.config @@ -1,3 +1,3 @@ - + diff --git a/CefSharp.BrowserSubprocess/app.manifest b/CefSharp.BrowserSubprocess/app.manifest index 8efbf5bf94..3b35ba9f61 100644 --- a/CefSharp.BrowserSubprocess/app.manifest +++ b/CefSharp.BrowserSubprocess/app.manifest @@ -8,7 +8,7 @@ xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - + @@ -27,12 +27,6 @@ - - - true/PM - - - - + - $(SolutionDir)CefSharp.Core.Runtime\bin.netcore\x64\$(Configuration)\CefSharp.Core.Runtime.dll + $(MSBuildThisFileDirectory)..\CefSharp.Core.Runtime\bin.netcore\x64\$(Configuration)\CefSharp.Core.Runtime.dll - + - $(SolutionDir)CefSharp.Core.Runtime\bin.netcore\arm64\$(Configuration)\CefSharp.Core.Runtime.dll + $(MSBuildThisFileDirectory)..\CefSharp.Core.Runtime\bin.netcore\arm64\$(Configuration)\CefSharp.Core.Runtime.dll - $(SolutionDir)CefSharp.Core.Runtime\bin.netcore\win32\$(Configuration)\CefSharp.Core.Runtime.dll + $(MSBuildThisFileDirectory)..\CefSharp.Core.Runtime\bin.netcore\win32\$(Configuration)\CefSharp.Core.Runtime.dll diff --git a/CefSharp.Core.Runtime.RefAssembly/CefSharp.Core.Runtime.netcore.cs b/CefSharp.Core.Runtime.RefAssembly/CefSharp.Core.Runtime.netcore.cs index f1cd6b3ffc..b1f57c359f 100644 --- a/CefSharp.Core.Runtime.RefAssembly/CefSharp.Core.Runtime.netcore.cs +++ b/CefSharp.Core.Runtime.RefAssembly/CefSharp.Core.Runtime.netcore.cs @@ -14,7 +14,6 @@ public partial class BrowserSettings : CefSharp.IBrowserSettings { public BrowserSettings() { } public BrowserSettings(bool autoDispose) { } - public virtual string AcceptLanguageList { get { throw null; } set { } } public virtual bool AutoDispose { get { throw null; } } public virtual uint BackgroundColor { get { throw null; } set { } } public virtual string CursiveFontFamily { get { throw null; } set { } } @@ -51,6 +50,8 @@ protected virtual void Dispose(bool A_0) { } public sealed partial class Cef { public Cef() { } + public static string ApiHashPlatform { get { throw null; } } + public static int ApiVersion { get { throw null; } } public static string CefCommitHash { get { throw null; } } public static string CefSharpVersion { get { throw null; } } public static string CefVersion { get { throw null; } } @@ -58,19 +59,20 @@ public Cef() { } public static bool CrashReportingEnabled { get { throw null; } } public static System.Threading.Tasks.TaskFactory FileThreadTaskFactory { get { throw null; } set { } } public static System.Threading.Tasks.TaskFactory IOThreadTaskFactory { get { throw null; } set { } } - public static bool IsInitialized { get { throw null; } } + public static bool? IsInitialized { get { throw null; } } public static bool IsShutdown { get { throw null; } } public static System.Threading.Tasks.TaskFactory UIThreadTaskFactory { get { throw null; } set { } } public static bool AddCrossOriginWhitelistEntry(string sourceOrigin, string targetProtocol, string targetDomain, bool allowTargetSubdomains) { throw null; } public static void AddDisposable(System.IDisposable item) { } + public static string ApiHash(int version, int entry) { throw null; } public static bool ClearCrossOriginWhitelist() { throw null; } public static bool ClearSchemeHandlerFactories() { throw null; } public static uint ColorSetARGB(uint a, uint r, uint g, uint b) { throw null; } public static bool CurrentlyOnThread(CefSharp.CefThreadIds threadId) { throw null; } public static void DoMessageLoopWork() { } - public static void EnableHighDPISupport() { } public static void EnableWaitForBrowsersToClose() { } public static int ExecuteProcess() { throw null; } + public static CefSharp.Enums.ResultCode GetExitCode() { throw null; } public static CefSharp.ICookieManager GetGlobalCookieManager() { throw null; } public static CefSharp.ICookieManager GetGlobalCookieManager(CefSharp.ICompletionCallback callback) { throw null; } public static CefSharp.IRequestContext GetGlobalRequestContext() { throw null; } @@ -80,6 +82,7 @@ public static void EnableWaitForBrowsersToClose() { } public static bool Initialize(CefSharp.Core.CefSettingsBase cefSettings, bool performDependencyCheck) { throw null; } public static bool Initialize(CefSharp.Core.CefSettingsBase cefSettings, bool performDependencyCheck, CefSharp.IApp cefApp) { throw null; } public static bool Initialize(CefSharp.Core.CefSettingsBase cefSettings, bool performDependencyCheck, CefSharp.IBrowserProcessHandler browserProcessHandler) { throw null; } + public static bool IsWindows10OrGreaterEx() { throw null; } public static CefSharp.UrlParts ParseUrl(string url) { throw null; } public static bool PostAction(CefSharp.CefThreadIds threadId, System.Action action) { throw null; } public static bool PostDelayedAction(CefSharp.CefThreadIds threadId, System.Action action, int delayInMs) { throw null; } @@ -104,7 +107,6 @@ public CefSettingsBase() { } public string CachePath { get { throw null; } set { } } public CefSharp.Internals.CommandLineArgDictionary CefCommandLineArgs { get { throw null; } } public System.Collections.Generic.IEnumerable CefCustomSchemes { get { throw null; } } - public bool ChromeRuntime { get { throw null; } set { } } public bool CommandLineArgsDisabled { get { throw null; } set { } } public bool CookieableSchemesExcludeDefaults { get { throw null; } set { } } public string CookieableSchemesList { get { throw null; } set { } } @@ -115,16 +117,13 @@ public CefSettingsBase() { } public string LogFile { get { throw null; } set { } } public CefSharp.LogSeverity LogSeverity { get { throw null; } set { } } public bool MultiThreadedMessageLoop { get { throw null; } set { } } - public bool PackLoadingDisabled { get { throw null; } set { } } public bool PersistSessionCookies { get { throw null; } set { } } - public bool PersistUserPreferences { get { throw null; } set { } } public int RemoteDebuggingPort { get { throw null; } set { } } public string ResourcesDirPath { get { throw null; } set { } } public string RootCachePath { get { throw null; } set { } } public int UncaughtExceptionStackSize { get { throw null; } set { } } public string UserAgent { get { throw null; } set { } } public string UserAgentProduct { get { throw null; } set { } } - public string UserDataPath { get { throw null; } set { } } public bool WindowlessRenderingEnabled { get { throw null; } set { } } public void Dispose() { } protected void Dispose(bool A_0) { } @@ -137,6 +136,7 @@ public partial class DragData : CefSharp.Internals.CefWrapper, CefSharp.IDragDat internal DragData() { } public virtual string FileName { get { throw null; } set { } } public virtual System.Collections.Generic.IList FileNames { get { throw null; } } + public virtual System.Collections.Generic.IList FilePaths { get { throw null; } } public virtual string FragmentBaseUrl { get { throw null; } set { } } public virtual string FragmentHtml { get { throw null; } set { } } public virtual string FragmentText { get { throw null; } set { } } @@ -271,23 +271,22 @@ public virtual void ClearHttpAuthCredentials(CefSharp.ICompletionCallback callba public virtual bool ClearSchemeHandlerFactories() { throw null; } public virtual void CloseAllConnections(CefSharp.ICompletionCallback callback) { } public static CefSharp.IRequestContext CreateContext(CefSharp.IRequestContext other, CefSharp.IRequestContextHandler requestContextHandler) { throw null; } - public virtual bool DidLoadExtension(string extensionId) { throw null; } public void Dispose() { } protected virtual void Dispose(bool A_0) { } ~RequestContext() { } public virtual System.Collections.Generic.IDictionary GetAllPreferences(bool includeDefaults) { throw null; } + public virtual CefSharp.Enums.ContentSettingValues GetContentSetting(string requestingUrl, string topLevelUrl, CefSharp.Enums.ContentSettingTypes contentType) { throw null; } public virtual CefSharp.ICookieManager GetCookieManager(CefSharp.ICompletionCallback callback) { throw null; } - public virtual CefSharp.IExtension GetExtension(string extensionId) { throw null; } - public virtual bool GetExtensions(out System.Collections.Generic.IList extensionIds) { throw null; } public virtual object GetPreference(string name) { throw null; } - public virtual bool HasExtension(string extensionId) { throw null; } + public virtual object GetWebsiteSetting(string requestingUrl, string topLevelUrl, CefSharp.Enums.ContentSettingTypes contentType) { throw null; } public virtual bool HasPreference(string name) { throw null; } public virtual bool IsSame(CefSharp.IRequestContext context) { throw null; } public virtual bool IsSharingWith(CefSharp.IRequestContext context) { throw null; } - public virtual void LoadExtension(string rootDirectory, string manifestJson, CefSharp.IExtensionHandler handler) { } public virtual bool RegisterSchemeHandlerFactory(string schemeName, string domainName, CefSharp.ISchemeHandlerFactory factory) { throw null; } public virtual System.Threading.Tasks.Task ResolveHostAsync(System.Uri origin) { throw null; } + public virtual void SetContentSetting(string requestingUrl, string topLevelUrl, CefSharp.Enums.ContentSettingTypes contentType, CefSharp.Enums.ContentSettingValues value) { } public virtual bool SetPreference(string name, object value, out string error) { throw null; } + public virtual void SetWebsiteSetting(string requestingUrl, string topLevelUrl, CefSharp.Enums.ContentSettingTypes contentType, object value) { } public virtual CefSharp.IRequestContext UnWrap() { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -299,7 +298,6 @@ public RequestContextSettings() { } public bool CookieableSchemesExcludeDefaults { get { throw null; } set { } } public string CookieableSchemesList { get { throw null; } set { } } public bool PersistSessionCookies { get { throw null; } set { } } - public bool PersistUserPreferences { get { throw null; } set { } } public void Dispose() { } protected virtual void Dispose(bool A_0) { } ~RequestContextSettings() { } @@ -324,6 +322,7 @@ public WindowInfo() { } public virtual bool ExternalBeginFrameEnabled { get { throw null; } set { } } public virtual int Height { get { throw null; } set { } } public virtual System.IntPtr ParentWindowHandle { get { throw null; } set { } } + public virtual CefSharp.CefRuntimeStyle RuntimeStyle { get { throw null; } set { } } public virtual bool SharedTextureEnabled { get { throw null; } set { } } public virtual uint Style { get { throw null; } set { } } public virtual int Width { get { throw null; } set { } } diff --git a/CefSharp.Core.Runtime/BrowserSettings.h b/CefSharp.Core.Runtime/BrowserSettings.h index 132c714ec4..1647c53db5 100644 --- a/CefSharp.Core.Runtime/BrowserSettings.h +++ b/CefSharp.Core.Runtime/BrowserSettings.h @@ -285,8 +285,8 @@ namespace CefSharp /// virtual property CefState Databases { - CefState get() { return (CefState)_browserSettings->databases; } - void set(CefState value) { _browserSettings->databases = (cef_state_t)value; } + CefState get() { return (CefState)_browserSettings->databases_deprecated; } + void set(CefState value) { _browserSettings->databases_deprecated = (cef_state_t)value; } } /// @@ -308,24 +308,10 @@ namespace CefSharp /// CefSettings.BackgroundColor value will be used. If the alpha component is fully transparent /// for a windowless (WPF/OffScreen) browser then transparent painting will be enabled. /// - virtual property uint32 BackgroundColor + virtual property uint32_t BackgroundColor { - uint32 get() { return _browserSettings->background_color; } - void set(uint32 value) { _browserSettings->background_color = value; } - } - - /// - /// Comma delimited ordered list of language codes without any whitespace that - /// will be used in the "Accept-Language" HTTP header. May be overridden on a - /// per-browser basis using the CefBrowserSettings.AcceptLanguageList value. - /// If both values are empty then "en-US,en" will be used. Can be overridden - /// for individual RequestContext instances via the - /// RequestContextSettings.AcceptLanguageList value. - /// - virtual property String^ AcceptLanguageList - { - String^ get() { return StringUtils::ToClr(_browserSettings->accept_language_list); } - void set(String^ value) { StringUtils::AssignNativeFromClr(_browserSettings->accept_language_list, value); } + uint32_t get() { return _browserSettings->background_color; } + void set(uint32_t value) { _browserSettings->background_color = value; } } /// diff --git a/CefSharp.Core.Runtime/Cef.h b/CefSharp.Core.Runtime/Cef.h index 9f42332782..c0a087ae89 100644 --- a/CefSharp.Core.Runtime/Cef.h +++ b/CefSharp.Core.Runtime/Cef.h @@ -9,6 +9,7 @@ #include "Stdafx.h" +#include "versionhelpers.h"; #include #include #include @@ -45,7 +46,7 @@ namespace CefSharp private: static Object^ _sync; - static bool _initialized = false; + static Nullable _initialized; static bool _hasShutdown = false; static HashSet^ _disposables; static int _initializedThreadId; @@ -85,9 +86,9 @@ namespace CefSharp /// Gets a value that indicates whether CefSharp is initialized. /// true if CefSharp is initialized; otherwise, false. - static property bool IsInitialized + static property Nullable IsInitialized { - bool get() + Nullable get() { return _initialized; } @@ -124,16 +125,46 @@ namespace CefSharp } } + /// + /// API version that will be compiled client-side. The experimental (unversioned) + /// API is selected by default. Clients can set the CEF_API_VERSION value in + /// their project configuration to configure an explicit API version. Unlike + /// the experimental API, explicit API versions are back/forward compatible with + /// a specific range of CEF versions. + /// + static property int ApiVersion + { + int get() + { + return CEF_API_VERSION; + } + } + + /// + /// API hashes for the selected CEF_API_VERSION. API hashes are created for + /// each version by analyzing CEF header files for C API type definitions. The + /// hash value will change when header files are modified in a way that may + /// cause binary incompatibility with other builds. + /// + static property String^ ApiHashPlatform + { + String^ get() + { + auto hash = CEF_API_HASH_PLATFORM; + + return gcnew String(hash); + } + } + /// Gets a value that indicates the Chromium version currently being used. /// The Chromium version. static property String^ ChromiumVersion { String^ get() { - // Need explicit cast here to avoid C4965 warning when the minor version is zero. - return String::Format("{0}.{1}.{2}.{3}", - CHROME_VERSION_MAJOR, (Object^)CHROME_VERSION_MINOR, - CHROME_VERSION_BUILD, CHROME_VERSION_PATCH); + auto version = gcnew Version(CHROME_VERSION_MAJOR, CHROME_VERSION_MINOR, CHROME_VERSION_BUILD, CHROME_VERSION_PATCH); + + return version->ToString(); } } @@ -149,6 +180,28 @@ namespace CefSharp } } + /// + /// Configures the CEF API version and returns API hashes for the libcef + /// library. The entry parameter describes which hash value will be returned: + /// + /// 0 - CEF_API_HASH_PLATFORM + /// 1 - CEF_API_HASH_UNIVERSAL (deprecated, same as CEF_API_HASH_PLATFORM) + /// 2 - CEF_COMMIT_HASH (from cef_version.h) + /// + /// + /// parameter should be CEF_API_VERSION and any changes to this value will be ignored after the first call to this method. + /// The entry parameter describes which hash value will be returned: + /// + /// returns API hashes for the libcef library. + /// The returned string is owned by the library and should not be freed. + /// + static String^ ApiHash(int version, int entry) + { + auto response = cef_api_hash(version, entry); + + return gcnew String(response); + } + /// /// Parse the specified url into its component parts. /// Uses a GURL to parse the Url. GURL is Google's URL parsing library. @@ -254,7 +307,7 @@ namespace CefSharp /// true if successful; otherwise, false. static bool Initialize(CefSettingsBase^ cefSettings, bool performDependencyCheck, IApp^ cefApp) { - if (_initialized) + if (_initialized.HasValue) { // NOTE: Can only initialize Cef once, to make this explicitly clear throw exception on subsiquent attempts throw gcnew Exception("Cef.Initialize can only be called once per process. This is a limitation of the underlying " + @@ -285,10 +338,9 @@ namespace CefSharp PathCheck::AssertAbsolute(cefSettings->LocalesDirPath, "CefSettings.LocalesDirPath"); PathCheck::AssertAbsolute(cefSettings->BrowserSubprocessPath, "CefSettings.BrowserSubprocessPath"); - if (performDependencyCheck) { - DependencyChecker::AssertAllDependenciesPresent(cefSettings->Locale, cefSettings->LocalesDirPath, cefSettings->ResourcesDirPath, cefSettings->PackLoadingDisabled, cefSettings->BrowserSubprocessPath); + DependencyChecker::AssertAllDependenciesPresent(cefSettings->Locale, cefSettings->LocalesDirPath, cefSettings->ResourcesDirPath, false, cefSettings->BrowserSubprocessPath); } else if (!File::Exists(cefSettings->BrowserSubprocessPath)) { @@ -523,6 +575,19 @@ namespace CefSharp return nullptr; } + /// + /// This function can optionally be called on the main application thread after + /// CefInitialize to retrieve the initialization exit code. When CefInitialize + /// returns true the exit code will be 0 (ResultCode.NormalExit). + /// Otherwise, see ResultCode for possible exit code values including + /// browser process initialization errors and normal early exit conditions + /// (such as ResultCode.NormalExitProcessNotified for process singleton relaunch behavior). + /// + static ResultCode GetExitCode() + { + return (ResultCode)CefGetExitCode(); + } + /// /// Called prior to calling Cef.Shutdown, this diposes of any remaning /// ChromiumWebBrowser instances. In WPF this is used from Dispatcher.ShutdownStarted @@ -554,11 +619,11 @@ namespace CefSharp /// static void Shutdown() { - if (_initialized) + if (_initialized.GetValueOrDefault()) { msclr::lock l(_sync); - if (_initialized) + if (_initialized.GetValueOrDefault()) { if (_initializedThreadId != Thread::CurrentThread->ManagedThreadId) { @@ -614,11 +679,11 @@ namespace CefSharp /// static void ShutdownWithoutChecks() { - if (_initialized) + if (_initialized.GetValueOrDefault()) { msclr::lock l(_sync); - if (_initialized) + if (_initialized.GetValueOrDefault()) { CefShutdown(); _initialized = false; @@ -638,16 +703,6 @@ namespace CefSharp return CefClearSchemeHandlerFactories(); } - /// - /// Call during process startup to enable High-DPI support on Windows 7 or newer. - /// Older versions of Windows should be left DPI-unaware because they do not - /// support DirectWrite and GDI fonts are kerned very badly. - /// - static void EnableHighDPISupport() - { - CefEnableHighDPISupport(); - } - /// /// Returns true if called on the specified CEF thread. /// @@ -684,7 +739,7 @@ namespace CefSharp /// Green /// Blue /// Returns the color. - static uint32 ColorSetARGB(uint32 a, uint32 r, uint32 g, uint32 b) + static uint32_t ColorSetARGB(uint32_t a, uint32_t r, uint32_t g, uint32_t b) { return CefColorSetARGB(a, r, g, b); } @@ -830,7 +885,7 @@ namespace CefSharp return; } - if (_initialized) + if (_initialized.HasValue) { throw gcnew Exception("Must be enabled before Cef.Initialize is called. "); } @@ -913,6 +968,11 @@ namespace CefSharp return CefPostTask((cef_thread_id_t)threadId, task); } + + static bool IsWindows10OrGreaterEx() + { + return IsWindows10OrGreater(); + } }; } } diff --git a/CefSharp.Core.Runtime/CefSettingsBase.h b/CefSharp.Core.Runtime/CefSettingsBase.h index f6cfe4f3dc..6d049095fc 100644 --- a/CefSharp.Core.Runtime/CefSettingsBase.h +++ b/CefSharp.Core.Runtime/CefSettingsBase.h @@ -84,18 +84,6 @@ namespace CefSharp CommandLineArgDictionary^ get() { return _cefCommandLineArgs; } } - /// - /// **Experimental** - /// Set to true to enable use of the Chrome runtime in CEF. This feature is - /// considered experimental and is not recommended for most users at this time. - /// See issue https://bitbucket.org/chromiumembedded/cef/issues/2969/support-chrome-windows-with-cef-callbacks for details. - /// - property bool ChromeRuntime - { - bool get() { return _cefSettings->chrome_runtime == 1; } - void set(bool value) { _cefSettings->chrome_runtime = value; } - } - /// /// Set to true to disable configuration of browser process features using standard CEF and Chromium command-line arguments. /// Configuration can still be specified using CEF data structures or by adding to CefCommandLineArgs. @@ -157,13 +145,22 @@ namespace CefSharp } /// - /// The root directory that all CefSettings.CachePath and RequestContextSettings.CachePath values must have in common. If this - /// value is empty and CefSettings.CachePath is non-empty then it will default to the CefSettings.CachePath value. - /// If this value is non-empty then it must be an absolute path. Failure to set this value correctly may result in the sandbox - /// blocking read/write access to the CachePath directory. NOTE: CefSharp does not implement the CHROMIUM SANDBOX. A non-empty - /// RootCachePath can be used in conjuncation with an empty CefSettings.CachePath in instances where you would like browsers - /// attached to the Global RequestContext (the default) created in "incognito mode" and instances created with a custom - /// RequestContext using a disk based cache. + /// The root directory for installation-specific data and the parent directory for profile-specific data. + /// All CachePath and RequestContextSettings.CachePath" values must have this parent directory + /// in common. If this value is empty and is non-empty then it will default to the + /// CachePath value. Any non-empty value must be an absolute path. If both values are empty then + /// the default platform-specific directory will be used ("AppData\Local\CEF\User Data" directory under the user + /// profile directory on Windows). + /// + /// **Use of the default directory is not recommended in production applications(see below).** + /// + /// Multiple application instances writing to the same RootCachePath directory could result in data corruption. + /// A process singleton lock based on the RootCachePath value is therefore used to protect against this. + /// This singleton behavior applies to all CEF-based applications using version 120 or newer. + /// You should customize RootCachePath for your application and implement IBrowserProcessHandler.OnAlreadyRunningAppRelaunch, + /// which will then be called on any app relaunch with the same RootCachePath value. + /// + /// Failure to set the RootCachePath value correctly may result in startup crashes or other unexpected behaviors /// property String^ RootCachePath { @@ -171,17 +168,6 @@ namespace CefSharp void set(String^ value) { StringUtils::AssignNativeFromClr(_cefSettings->root_cache_path, value); } } - /// - /// The location where user data such as the Widevine CDM module and spell checking dictionary files will be stored on disk. - /// If this value is empty then "Local Settings\Application Data\CEF\User Data" directory under the user profile directory - /// will be used. If this value is non-empty then it must be an absolute path. - /// - property String^ UserDataPath - { - String^ get() { return StringUtils::ToClr(_cefSettings->user_data_path); } - void set(String^ value) { StringUtils::AssignNativeFromClr(_cefSettings->user_data_path, value); } - } - /// /// The locale string that will be passed to WebKit. If empty the default locale of "en-US" will be used. Also configurable using /// the "lang" command-line switch. @@ -246,17 +232,6 @@ namespace CefSharp void set(String^ value) { StringUtils::AssignNativeFromClr(_cefSettings->javascript_flags, value); } } - /// - /// Set to true to disable loading of pack files for resources and locales. A resource bundle handler must be provided for the - /// browser and render processes via CefApp::GetResourceBundleHandler() if loading of pack files is disabled. Also configurable - /// using the "disable-pack-loading" command- line switch. - /// - property bool PackLoadingDisabled - { - bool get() { return _cefSettings->pack_loading_disabled == 1; } - void set(bool value) { _cefSettings->pack_loading_disabled = value; } - } - /// /// Value that will be inserted as the product portion of the default User-Agent string. If empty the Chromium product version /// will be used. If UserAgent is specified this value will be ignored. Also configurable using the "user-agent-product" command- @@ -311,11 +286,15 @@ namespace CefSharp } /// - /// To persist session cookies (cookies without an expiry date or validity interval) by default when using the global cookie - /// manager set this value to true. Session cookies are generally intended to be transient and most Web browsers do not persist - /// them. A CachePath value must also be specified to enable this feature. Also configurable using the "persist-session-cookies" - /// command-line switch. Can be overridden for individual RequestContext instances via the - /// RequestContextSettings.PersistSessionCookies value. + /// The directory where data for the global browser cache will be stored on disk. + /// If this value is non-empty then it must be an absolute path that is either equal to or a child directory + /// of RootCachePath. If this value is empty then browsers will be created in "incognito mode" + /// where in-memory caches are used for storage and no profile-specific data is persisted to disk + /// (installation-specific data will still be persisted in RootCachePath). HTML5 databases + /// such as localStorage will only persist across sessions if a cache path is specified. + /// Can be overridden for individual RequestContext instances via the RequestContextSettings.CachePath value. + /// Any child directory value will be ignored and the "default" profile (also a child directory) will be used + /// instead. /// property bool PersistSessionCookies { @@ -323,17 +302,6 @@ namespace CefSharp void set(bool value) { _cefSettings->persist_session_cookies = value; } } - /// - /// To persist user preferences as a JSON file in the cache path directory set this value to true. A CachePath value must also be - /// specified to enable this feature. Also configurable using the "persist-user-preferences" command-line switch. Can be - /// overridden for individual RequestContext instances via the RequestContextSettings.PersistUserPreferences value. - /// - property bool PersistUserPreferences - { - bool get() { return _cefSettings->persist_user_preferences == 1; } - void set(bool value) { _cefSettings->persist_user_preferences = value; } - } - /// /// Comma delimited ordered list of language codes without any whitespace that will be used in the "Accept-Language" HTTP header. /// May be set globally using the CefSettings.AcceptLanguageList value. If both values are empty then "en-US,en" will be used. @@ -352,10 +320,10 @@ namespace CefSharp /// default value of opaque white be used. If the alpha component is fully transparent for a windowless (WPF/OffScreen) browser /// then transparent painting will be enabled. /// - property uint32 BackgroundColor + property uint32_t BackgroundColor { - uint32 get() { return _cefSettings->background_color; } - void set(uint32 value) { _cefSettings->background_color = value; } + uint32_t get() { return _cefSettings->background_color; } + void set(uint32_t value) { _cefSettings->background_color = value; } } /// diff --git a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj index 0a8d6744bc..fff6c821f1 100644 --- a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj +++ b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj @@ -1,6 +1,6 @@  - + @@ -36,7 +36,8 @@ CefSharp.Core.Runtime CefSharp NetCoreCProj - netcoreapp3.1 + net6.0 + 10.0.10240.0 @@ -177,6 +178,7 @@ Stdafx.h true true + stdcpp17 false @@ -210,6 +212,7 @@ ProgramDatabase Stdafx.h true + stdcpp17 false @@ -240,6 +243,7 @@ ProgramDatabase Stdafx.h true + stdcpp17 false @@ -269,6 +273,7 @@ true true true + stdcpp17 libcef.lib;libcef_dll_wrapper.lib;%(AdditionalDependencies) @@ -292,6 +297,7 @@ ProgramDatabase true true + stdcpp17 libcef.lib;libcef_dll_wrapper.lib;%(AdditionalDependencies) @@ -313,6 +319,7 @@ ProgramDatabase true true + stdcpp17 libcef.lib;libcef_dll_wrapper.lib;%(AdditionalDependencies) @@ -329,7 +336,6 @@ - @@ -368,9 +374,6 @@ - - - diff --git a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj.filters b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj.filters index b9a360b8bd..f225404bd0 100644 --- a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj.filters +++ b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.netcore.vcxproj.filters @@ -62,9 +62,6 @@ Source Files - - Source Files - Source Files @@ -124,7 +121,7 @@ Header Files - + Header Files @@ -262,15 +259,6 @@ Header Files - - Header Files - - - Header Files - - - Header Files - Header Files diff --git a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj index 3a6bc7034f..8c6487711b 100644 --- a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj +++ b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj @@ -1,6 +1,6 @@  - + @@ -26,7 +26,7 @@ {7B495581-2271-4F41-9476-ACB86E8C864F} CefSharp ManagedCProj - v4.5.2 + v4.6.2 @@ -130,6 +130,7 @@ Stdafx.h true true + stdcpp17 false @@ -160,6 +161,7 @@ ProgramDatabase Stdafx.h true + stdcpp17 false @@ -186,6 +188,7 @@ true true true + stdcpp17 opengl32.lib;glu32.lib;libcef.lib;libcef_dll_wrapper.lib;%(AdditionalDependencies) @@ -206,6 +209,7 @@ ProgramDatabase true true + stdcpp17 opengl32.lib;glu32.lib;libcef.lib;libcef_dll_wrapper.lib;%(AdditionalDependencies) @@ -235,7 +239,6 @@ - @@ -273,9 +276,6 @@ - - - diff --git a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj.filters b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj.filters index 5f6a9aec42..203ef52396 100644 --- a/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj.filters +++ b/CefSharp.Core.Runtime/CefSharp.Core.Runtime.vcxproj.filters @@ -62,9 +62,6 @@ Source Files - - Source Files - Source Files @@ -256,15 +253,6 @@ Header Files - - Header Files - - - Header Files - - - Header Files - Header Files diff --git a/CefSharp.Core.Runtime/DragData.h b/CefSharp.Core.Runtime/DragData.h index ea9b6791cb..48aa18fe28 100644 --- a/CefSharp.Core.Runtime/DragData.h +++ b/CefSharp.Core.Runtime/DragData.h @@ -90,6 +90,18 @@ namespace CefSharp } } + //TODO: Vector is a pointer, so can potentially be updated (items may be possibly removed) + virtual property IList^ FilePaths + { + IList^ get() + { + auto paths = vector(); + _wrappedDragData->GetFilePaths(paths); + + return StringUtils::ToClr(paths); + } + } + virtual property String^ FragmentBaseUrl { String^ get() diff --git a/CefSharp.Core.Runtime/Internals/CefBrowserHostWrapper.cpp b/CefSharp.Core.Runtime/Internals/CefBrowserHostWrapper.cpp index 143f278c37..5b8da2a87b 100644 --- a/CefSharp.Core.Runtime/Internals/CefBrowserHostWrapper.cpp +++ b/CefSharp.Core.Runtime/Internals/CefBrowserHostWrapper.cpp @@ -9,7 +9,6 @@ #include "include\cef_parser.h" #include "Cef.h" -#include "CefExtensionWrapper.h" #include "CefTaskScheduler.h" #include "DragData.h" #include "CefRunFileDialogCallbackAdapter.h" @@ -134,10 +133,7 @@ Task^ CefBrowserHostWrapper::GetZoomLevelAsync() if (CefCurrentlyOn(TID_UI)) { - auto taskSource = gcnew TaskCompletionSource(); - - CefSharp::Internals::TaskExtensions::TrySetResultAsync(taskSource, GetZoomLevelOnUI()); - return taskSource->Task; + return Task::FromResult(GetZoomLevelOnUI()); } return Cef::UIThreadTaskFactory->StartNew(gcnew Func(this, &CefBrowserHostWrapper::GetZoomLevelOnUI)); } @@ -290,20 +286,6 @@ void CefBrowserHostWrapper::ReplaceMisspelling(String^ word) _browserHost->ReplaceMisspelling(StringUtils::ToNative(word)); } -IExtension^ CefBrowserHostWrapper::Extension::get() -{ - ThrowIfDisposed(); - - auto extension = _browserHost->GetExtension(); - - if (extension.get()) - { - return gcnew CefExtensionWrapper(_browserHost->GetExtension()); - } - - return nullptr; -} - void CefBrowserHostWrapper::RunFileDialog(CefFileDialogMode mode, String^ title, String^ defaultFilePath, IList^ acceptFilters, IRunFileDialogCallback^ callback) { ThrowIfDisposed(); @@ -350,7 +332,7 @@ void CefBrowserHostWrapper::SendKeyEvent(KeyEvent keyEvent) CefKeyEvent nativeKeyEvent; nativeKeyEvent.focus_on_editable_field = keyEvent.FocusOnEditableField == 1; nativeKeyEvent.is_system_key = keyEvent.IsSystemKey == 1; - nativeKeyEvent.modifiers = (uint32)keyEvent.Modifiers; + nativeKeyEvent.modifiers = (uint32_t)keyEvent.Modifiers; nativeKeyEvent.type = (cef_key_event_type_t)keyEvent.Type; nativeKeyEvent.native_key_code = keyEvent.NativeKeyCode; nativeKeyEvent.windows_key_code = keyEvent.WindowsKeyCode; @@ -436,7 +418,7 @@ void CefBrowserHostWrapper::SendMouseWheelEvent(MouseEvent mouseEvent, int delta CefMouseEvent m; m.x = mouseEvent.X; m.y = mouseEvent.Y; - m.modifiers = (uint32)mouseEvent.Modifiers; + m.modifiers = (uint32_t)mouseEvent.Modifiers; _browserHost->SendMouseWheelEvent(m, deltaX, deltaY); } @@ -450,7 +432,7 @@ void CefBrowserHostWrapper::SendTouchEvent(TouchEvent evt) { CefTouchEvent e; e.id = evt.Id; - e.modifiers = (uint32)evt.Modifiers; + e.modifiers = (uint32_t)evt.Modifiers; e.pointer_type = (cef_pointer_type_t)evt.PointerType; e.pressure = evt.Pressure; e.radius_x = evt.RadiusX; @@ -485,13 +467,6 @@ void CefBrowserHostWrapper::Invalidate(PaintElementType type) _browserHost->Invalidate((CefBrowserHost::PaintElementType)type); } -bool CefBrowserHostWrapper::IsBackgroundHost::get() -{ - ThrowIfDisposed(); - - return _browserHost->IsBackgroundHost(); -} - void CefBrowserHostWrapper::ImeSetComposition(String^ text, cli::array^ underlines, Nullable replacementRange, Nullable selectionRange) { ThrowIfDisposed(); @@ -562,7 +537,7 @@ void CefBrowserHostWrapper::SendMouseClickEvent(MouseEvent mouseEvent, MouseButt CefMouseEvent m; m.x = mouseEvent.X; m.y = mouseEvent.Y; - m.modifiers = (uint32)mouseEvent.Modifiers; + m.modifiers = (uint32_t)mouseEvent.Modifiers; _browserHost->SendMouseClickEvent(m, (CefBrowserHost::MouseButtonType) mouseButtonType, mouseUp, clickCount); } @@ -574,7 +549,7 @@ void CefBrowserHostWrapper::SendMouseMoveEvent(MouseEvent mouseEvent, bool mouse CefMouseEvent m; m.x = mouseEvent.X; m.y = mouseEvent.Y; - m.modifiers = (uint32)mouseEvent.Modifiers; + m.modifiers = (uint32_t)mouseEvent.Modifiers; _browserHost->SendMouseMoveEvent(m, mouseLeave); } @@ -698,7 +673,7 @@ CefMouseEvent CefBrowserHostWrapper::GetCefMouseEvent(MouseEvent mouseEvent) CefMouseEvent cefMouseEvent; cefMouseEvent.x = mouseEvent.X; cefMouseEvent.y = mouseEvent.Y; - cefMouseEvent.modifiers = (uint32)mouseEvent.Modifiers; + cefMouseEvent.modifiers = (uint32_t)mouseEvent.Modifiers; return cefMouseEvent; } diff --git a/CefSharp.Core.Runtime/Internals/CefBrowserHostWrapper.h b/CefSharp.Core.Runtime/Internals/CefBrowserHostWrapper.h index 2f2cd093b9..ca6bb5f5ca 100644 --- a/CefSharp.Core.Runtime/Internals/CefBrowserHostWrapper.h +++ b/CefSharp.Core.Runtime/Internals/CefBrowserHostWrapper.h @@ -81,11 +81,6 @@ namespace CefSharp virtual void AddWordToDictionary(String^ word); virtual void ReplaceMisspelling(String^ word); - virtual property IExtension^ Extension - { - IExtension^ get(); - } - virtual void RunFileDialog(CefFileDialogMode mode, String^ title, String^ defaultFilePath, IList^ acceptFilters, IRunFileDialogCallback^ callback); virtual void Find(String^ searchText, bool forward, bool matchCase, bool findNext); @@ -102,11 +97,6 @@ namespace CefSharp virtual void Invalidate(PaintElementType type); - virtual property bool IsBackgroundHost - { - bool get(); - } - virtual void ImeSetComposition(String^ text, cli::array^ underlines, Nullable replacementRange, Nullable selectionRange); virtual void ImeCommitText(String^ text, Nullable replacementRange, int relativeCursorPos); virtual void ImeFinishComposingText(bool keepSelection); diff --git a/CefSharp.Core.Runtime/Internals/CefBrowserWrapper.cpp b/CefSharp.Core.Runtime/Internals/CefBrowserWrapper.cpp index db2d8023ec..4d9e2af1bd 100644 --- a/CefSharp.Core.Runtime/Internals/CefBrowserWrapper.cpp +++ b/CefSharp.Core.Runtime/Internals/CefBrowserWrapper.cpp @@ -192,11 +192,11 @@ IFrame^ CefBrowserWrapper::FocusedFrame::get() // Returns the frame with the specified identifier, or NULL if not found. /// /*--cef(capi_name=get_frame_byident)--*/ -IFrame^ CefBrowserWrapper::GetFrame(Int64 identifier) +IFrame^ CefBrowserWrapper::GetFrameByIdentifier(String^ identifier) { ThrowIfDisposed(); - auto frame = _browser->GetFrame(identifier); + auto frame = _browser->GetFrameByIdentifier(StringUtils::ToNative(identifier)); if (frame.get()) { @@ -210,11 +210,11 @@ IFrame^ CefBrowserWrapper::GetFrame(Int64 identifier) // Returns the frame with the specified name, or NULL if not found. /// /*--cef(optional_param=name)--*/ -IFrame^ CefBrowserWrapper::GetFrame(String^ name) +IFrame^ CefBrowserWrapper::GetFrameByName(String^ name) { ThrowIfDisposed(); - auto frame = _browser->GetFrame(StringUtils::ToNative(name)); + auto frame = _browser->GetFrameByName(StringUtils::ToNative(name)); if (frame.get()) { @@ -238,16 +238,16 @@ int CefBrowserWrapper::GetFrameCount() // Returns the identifiers of all existing frames. /// /*--cef(count_func=identifiers:GetFrameCount)--*/ -List^ CefBrowserWrapper::GetFrameIdentifiers() +List^ CefBrowserWrapper::GetFrameIdentifiers() { ThrowIfDisposed(); - std::vector identifiers; + std::vector identifiers; _browser->GetFrameIdentifiers(identifiers); - List^ results = gcnew List(static_cast(identifiers.size())); + List^ results = gcnew List(static_cast(identifiers.size())); for (UINT i = 0; i < identifiers.size(); i++) { - results->Add(identifiers[i]); + results->Add(StringUtils::ToClr(identifiers[i])); } return results; } @@ -266,6 +266,24 @@ List^ CefBrowserWrapper::GetFrameNames() return StringUtils::ToClr(names); } +IReadOnlyCollection^ CefBrowserWrapper::GetAllFrames() +{ + std::vector identifiers; + _browser->GetFrameIdentifiers(identifiers); + + auto results = gcnew List(static_cast(identifiers.size())); + for (UINT i = 0; i < identifiers.size(); i++) + { + auto frame = _browser->GetFrameByIdentifier(identifiers[i]); + + if (frame.get()) + { + results->Add(gcnew CefFrameWrapper(frame)); + } + } + return results; +} + MCefRefPtr CefBrowserWrapper::Browser::get() { return _browser; diff --git a/CefSharp.Core.Runtime/Internals/CefBrowserWrapper.h b/CefSharp.Core.Runtime/Internals/CefBrowserWrapper.h index 0d0fd8920a..e7a1f5c4a4 100644 --- a/CefSharp.Core.Runtime/Internals/CefBrowserWrapper.h +++ b/CefSharp.Core.Runtime/Internals/CefBrowserWrapper.h @@ -166,13 +166,13 @@ namespace CefSharp // Returns the frame with the specified identifier, or NULL if not found. /// /*--cef(capi_name=get_frame_byident)--*/ - virtual IFrame^ GetFrame(Int64 identifier); + virtual IFrame^ GetFrameByIdentifier(String^ identifier); /// // Returns the frame with the specified name, or NULL if not found. /// /*--cef(optional_param=name)--*/ - virtual IFrame^ GetFrame(String^ name); + virtual IFrame^ GetFrameByName(String^ name); /// // Returns the number of frames that currently exist. @@ -184,13 +184,15 @@ namespace CefSharp // Returns the identifiers of all existing frames. /// /*--cef(count_func=identifiers:GetFrameCount)--*/ - virtual List^ GetFrameIdentifiers(); + virtual List^ GetFrameIdentifiers(); /// // Returns the names of all existing frames. /// /*--cef()--*/ virtual List^ GetFrameNames(); + + virtual IReadOnlyCollection^ GetAllFrames(); }; } } diff --git a/CefSharp.Core.Runtime/Internals/CefExtensionHandlerAdapter.h b/CefSharp.Core.Runtime/Internals/CefExtensionHandlerAdapter.h deleted file mode 100644 index 14fa5278d6..0000000000 --- a/CefSharp.Core.Runtime/Internals/CefExtensionHandlerAdapter.h +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright © 2018 The CefSharp Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. - -#pragma once - -#include "Stdafx.h" - -#include "include\cef_extension.h" -#include "include\cef_extension_handler.h" -#include "BrowserSettings.h" -#include "CefExtensionWrapper.h" -#include "CefBrowserWrapper.h" -#include "CefGetExtensionResourceCallbackWrapper.h" -#include "WindowInfo.h" - -using namespace CefSharp; - -namespace CefSharp -{ - namespace Internals - { - private class CefExtensionHandlerAdapter : public CefExtensionHandler - { - gcroot _handler; - - public: - CefExtensionHandlerAdapter(IExtensionHandler^ handler) - : _handler(handler) - { - } - - ~CefExtensionHandlerAdapter() - { - delete _handler; - _handler = nullptr; - } - - /// - // Called if the CefRequestContext::LoadExtension request fails. |result| will - // be the error code. - /// - /*--cef()--*/ - void OnExtensionLoadFailed(cef_errorcode_t result) override - { - _handler->OnExtensionLoadFailed((CefErrorCode)result); - } - - /// - // Called if the CefRequestContext::LoadExtension request succeeds. - // |extension| is the loaded extension. - /// - /*--cef()--*/ - void OnExtensionLoaded(CefRefPtr extension) override - { - //TODO: Should this be auto disposed? - _handler->OnExtensionLoaded(gcnew CefExtensionWrapper(extension)); - } - - /// - // Called after the CefExtension::Unload request has completed. - /// - /*--cef()--*/ - void OnExtensionUnloaded(CefRefPtr extension) override - { - //TODO: Add comment to interface saying extension is only valid within the scope - //of this method as it's auto disposed - CefExtensionWrapper wrapper(extension); - _handler->OnExtensionUnloaded(%wrapper); - } - - /// - // Called when an extension needs a browser to host a background script - // specified via the "background" manifest key. The browser will have no - // visible window and cannot be displayed. |extension| is the extension that - // is loading the background script. |url| is an internally generated - // reference to an HTML page that will be used to load the background script - // via a

testing

"; - int scriptId = 0; - - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - //Load a dummy page initially so we can then add our script using - //Page.AddScriptToEvaluateOnNewDocument (via DevTools) - using (var browser = new ChromiumWebBrowser(new HtmlString("Initial Load"), useLegacyRenderHandler: false)) - { - await browser.WaitForInitialLoadAsync(); - - using (var devToolsClient = browser.GetDevToolsClient()) - { - var result = await devToolsClient.Page.AddScriptToEvaluateOnNewDocumentAsync(Script); - scriptId = int.Parse(result.Identifier); - - //We must use Page.Enable for the script to be added - await devToolsClient.Page.EnableAsync(); - } - - browser.LoadHtml(rawHtml); - - browser.JavascriptMessageReceived += (o, e) => - { - tcs.SetResult((string)e.Message); - }; - - var responseFromJavascript = await tcs.Task; - - Assert.True(scriptId > 0); - Assert.Equal(eventToRaise, responseFromJavascript); - } - } - } -} diff --git a/CefSharp.Test/PostMessage/PostMessageTests.cs b/CefSharp.Test/PostMessage/PostMessageTests.cs new file mode 100644 index 0000000000..d0e08717f1 --- /dev/null +++ b/CefSharp.Test/PostMessage/PostMessageTests.cs @@ -0,0 +1,101 @@ +// Copyright © 2020 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace CefSharp.Test.PostMessage +{ + //NOTE: All Test classes must be part of this collection as it manages the Cef Initialize/Shutdown lifecycle + [Collection(CefSharpFixtureCollection.Key)] + public class PostMessageTests : BrowserTests + { + private readonly ITestOutputHelper output; + private readonly CefSharpFixture fixture; + + public PostMessageTests(ITestOutputHelper output, CefSharpFixture fixture) + { + this.fixture = fixture; + this.output = output; + } + + [Fact] + public async Task ShouldWork() + { + AssertInitialLoadComplete(); + + var evt = await Assert.RaisesAsync( + a => Browser.JavascriptMessageReceived += a, + a => Browser.JavascriptMessageReceived -= a, + () => Browser.EvaluateScriptAsync("cefSharp.postMessage('test');")); + + Assert.NotNull(evt); + Assert.Equal("test", evt.Arguments.Message); + } + + [Fact] + public async Task ShouldWorkWithJavascriptCallback() + { + const string expected = "Echo"; + + AssertInitialLoadComplete(); + + var evt = await Assert.RaisesAsync( + a => Browser.JavascriptMessageReceived += a, + a => Browser.JavascriptMessageReceived -= a, + () => Browser.EvaluateScriptAsync("cefSharp.postMessage({ 'Type': 'Update', Data: { 'Property': 123 }, 'Callback': (p1) => { return p1; } });")); + + Assert.NotNull(evt); + + dynamic msg = evt.Arguments.Message; + var callback = (IJavascriptCallback)msg.Callback; + var response = await callback.ExecuteAsync(expected); + + Assert.True(response.Success); + Assert.Equal(expected, response.Result); + } + + [Theory] + [InlineData("Event", "Event1")] + [InlineData("Event", "Event2")] + [InlineData("CustomEvent", "Event1")] + [InlineData("CustomEvent", "Event2")] + public async Task ShouldWorkForCustomEvent(string jsEventObject, string expected) + { + const string Script = @" + const postMessageHandler = e => { cefSharp.postMessage(e.type); }; + window.addEventListener(""Event1"", postMessageHandler, false); + window.addEventListener(""Event2"", postMessageHandler, false);"; + + string rawHtml = $"

testing

"; + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + //Make sure to load an initial page so we can then add our script using + //Page.AddScriptToEvaluateOnNewDocument (via DevTools) + + using (var devToolsClient = Browser.GetDevToolsClient()) + { + var result = await devToolsClient.Page.AddScriptToEvaluateOnNewDocumentAsync(Script); + var scriptId = int.Parse(result.Identifier); + + //We must use Page.Enable for the script to be added + await devToolsClient.Page.EnableAsync(); + + Browser.LoadHtml(rawHtml); + + Browser.JavascriptMessageReceived += (o, e) => + { + tcs.SetResult((string)e.Message); + }; + + var actual = await tcs.Task; + + Assert.True(scriptId > 0); + Assert.Equal(expected, actual); + } + } + } +} diff --git a/CefSharp.Test/PrintToPdf/PrintToPdfTests.cs b/CefSharp.Test/PrintToPdf/PrintToPdfTests.cs new file mode 100644 index 0000000000..9a5a0f7560 --- /dev/null +++ b/CefSharp.Test/PrintToPdf/PrintToPdfTests.cs @@ -0,0 +1,59 @@ +using System.Threading.Tasks; +using Xunit.Abstractions; +using Xunit; +using System.IO; +using CefSharp.OffScreen; +using CefSharp.Example; +using System; + +namespace CefSharp.Test.PrintToPdf +{ + [Collection(CefSharpFixtureCollection.Key)] + public class PrintToPdfTests : BrowserTests + { + private readonly ITestOutputHelper output; + private readonly CefSharpFixture fixture; + + public PrintToPdfTests(ITestOutputHelper output, CefSharpFixture fixture) + { + this.fixture = fixture; + this.output = output; + } + + [Fact] + public async Task ShouldWork() + { + AssertInitialLoadComplete(); + + var tempFile = Path.Combine(Path.GetTempPath(), "test.pdf"); + + if(File.Exists(tempFile)) + { + File.Delete(tempFile); + } + + var success = await Browser.PrintToPdfAsync(tempFile); + + Assert.True(success, $"PDF Generation Failed {tempFile}"); + Assert.True(File.Exists(tempFile), $"PDF File not found {tempFile}"); + } + + [Fact] + public async Task ShouldFailIfPageNotLoaded() + { + var tempFile = Path.Combine(Path.GetTempPath(), "test.pdf"); + + if (File.Exists(tempFile)) + { + File.Delete(tempFile); + } + + using (var browser = new ChromiumWebBrowser(CefExample.HelloWorldUrl, automaticallyCreateBrowser: false)) + { + var exception = await Assert.ThrowsAsync(async () => await browser.PrintToPdfAsync(tempFile)); + + Assert.Equal(WebBrowserExtensions.BrowserNullExceptionString, exception.Message); + } + } + } +} diff --git a/CefSharp.Test/Properties/AssemblyInfo.cs b/CefSharp.Test/Properties/AssemblyInfo.cs index 4d34951997..7948f084c1 100644 --- a/CefSharp.Test/Properties/AssemblyInfo.cs +++ b/CefSharp.Test/Properties/AssemblyInfo.cs @@ -19,4 +19,4 @@ [assembly: CLSCompliant(AssemblyInfo.ClsCompliant)] [assembly: CollectionBehavior(DisableTestParallelization = true, MaxParallelThreads = 1)] -[assembly: TestCaseOrderer("CefSharp.Test.CefSharpTestCaseOrderer", "CefSharp.Test")] +//[assembly: TestCaseOrderer("CefSharp.Test.CefSharpTestCaseOrderer", "CefSharp.Test")] diff --git a/CefSharp.Test/RequestContextIsolatedBrowserTests.cs b/CefSharp.Test/RequestContextIsolatedBrowserTests.cs new file mode 100644 index 0000000000..5ac6dfced1 --- /dev/null +++ b/CefSharp.Test/RequestContextIsolatedBrowserTests.cs @@ -0,0 +1,23 @@ +// Copyright © 2023 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System.Threading.Tasks; +using CefSharp.Example; +using CefSharp.OffScreen; +using Xunit; + +namespace CefSharp.Test +{ + /// + /// Each browser instance will be created with it's own + /// using an InMemory cache + /// + public abstract class RequestContextIsolatedBrowserTests : BrowserTests + { + protected RequestContextIsolatedBrowserTests() + { + RequestContextIsolated = true; + } + } +} diff --git a/CefSharp.Test/SchemeHandler/FolderSchemeHandlerFactoryTests.cs b/CefSharp.Test/SchemeHandler/FolderSchemeHandlerFactoryTests.cs index 6568c945f5..a75af1fee9 100644 --- a/CefSharp.Test/SchemeHandler/FolderSchemeHandlerFactoryTests.cs +++ b/CefSharp.Test/SchemeHandler/FolderSchemeHandlerFactoryTests.cs @@ -28,7 +28,7 @@ public FolderSchemeHandlerFactoryTests(ITestOutputHelper output, CefSharpFixture } [Fact] - public async Task CanWork() + public async Task ShouldWork() { const string expected = "https://folderschemehandlerfactory.test/"; @@ -57,7 +57,7 @@ public async Task CanWork() } [Fact] - public async Task CanDeleteFileAfterLoading() + public async Task ShouldAllowFileDeletionAfterLoading() { const string expected = "https://folderschemehandlerfactory.test/"; const string html = "I'm going to be deleted after use!"; diff --git a/CefSharp.Test/Selector/WaitForSelectorAsyncTests.cs b/CefSharp.Test/Selector/WaitForSelectorAsyncTests.cs index 0459ed2ba0..42b4dcd6df 100644 --- a/CefSharp.Test/Selector/WaitForSelectorAsyncTests.cs +++ b/CefSharp.Test/Selector/WaitForSelectorAsyncTests.cs @@ -1,3 +1,7 @@ +// Copyright © 2022 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + using Xunit.Abstractions; using Xunit; using System.Threading.Tasks; diff --git a/CefSharp.Test/UrlRequest/UrlRequestTests.cs b/CefSharp.Test/UrlRequest/UrlRequestTests.cs new file mode 100644 index 0000000000..03ac74f46a --- /dev/null +++ b/CefSharp.Test/UrlRequest/UrlRequestTests.cs @@ -0,0 +1,57 @@ +// Copyright © 2022 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace CefSharp.Test.UrlRequest +{ + //NOTE: All Test classes must be part of this collection as it manages the Cef Initialize/Shutdown lifecycle + [Collection(CefSharpFixtureCollection.Key)] + public class UrlRequestTests + { + private readonly ITestOutputHelper output; + private readonly CefSharpFixture fixture; + + public UrlRequestTests(ITestOutputHelper output, CefSharpFixture fixture) + { + this.fixture = fixture; + this.output = output; + } + + [Fact] + public async Task ShouldWork() + { + var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + IUrlRequest urlRequest = null; + int statusCode = -1; + + //Can be created on any valid CEF Thread, here we'll use the CEF UI Thread + await Cef.UIThreadTaskFactory.StartNew(delegate + { + var requestClient = new Example.UrlRequestClient((IUrlRequest req, byte[] responseBody) => + { + statusCode = req.Response.StatusCode; + taskCompletionSource.TrySetResult(Encoding.UTF8.GetString(responseBody)); + }); + + var request = new Request + { + Method = "GET", + Url = "https://code.jquery.com/jquery-3.4.1.min.js" + }; + + //Global RequestContext will be used + urlRequest = new CefSharp.UrlRequest(request, requestClient); + }); + + var stringResult = await taskCompletionSource.Task; + + Assert.True(!string.IsNullOrEmpty(stringResult)); + Assert.Equal(200, statusCode); + } + } +} diff --git a/CefSharp.Test/WebBrowserTestExtensions.cs b/CefSharp.Test/WebBrowserTestExtensions.cs index 227eb3f7c1..750d8d06f1 100644 --- a/CefSharp.Test/WebBrowserTestExtensions.cs +++ b/CefSharp.Test/WebBrowserTestExtensions.cs @@ -5,6 +5,7 @@ using System; using System.Reflection; using System.Threading.Tasks; +using CefSharp.Internals; using CefSharp.OffScreen; namespace CefSharp.Test @@ -60,8 +61,6 @@ public static Task LoadRequestAsync(this IWebBrowser brows throw new ArgumentNullException("request"); } - //If using .Net 4.6 then use TaskCreationOptions.RunContinuationsAsynchronously - //and switch to tcs.TrySetResult below - no need for the custom extension method var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); EventHandler loadErrorHandler = null; @@ -114,10 +113,9 @@ public static Task LoadRequestAsync(this IWebBrowser brows }; browser.LoadingStateChanged += loadingStateChangeHandler; - browser.GetMainFrame().LoadRequest(request); - return tcs.Task; + return TaskTimeoutExtensions.WaitAsync(tcs.Task, TimeSpan.FromSeconds(30)); } public static Task CreateBrowserAndWaitForQUnitTestExeuctionToComplete(this ChromiumWebBrowser browser) diff --git a/CefSharp.Test/WinForms/RequestContextTests.cs b/CefSharp.Test/WinForms/RequestContextTests.cs new file mode 100644 index 0000000000..6cae221ac0 --- /dev/null +++ b/CefSharp.Test/WinForms/RequestContextTests.cs @@ -0,0 +1,69 @@ +// Copyright © 2017 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; +using System.Threading.Tasks; +using CefSharp.Example; +using CefSharp.WinForms; +using Xunit; +using Xunit.Abstractions; + +namespace CefSharp.Test.WinForms +{ + //NOTE: All Test classes must be part of this collection as it manages the Cef Initialize/Shutdown lifecycle + [Collection(CefSharpFixtureCollection.Key)] + public class RequestContextTests + { + private readonly ITestOutputHelper output; + private readonly CefSharpFixture fixture; + + public RequestContextTests(ITestOutputHelper output, CefSharpFixture fixture) + { + this.fixture = fixture; + this.output = output; + } + + [WinFormsFact] + public async Task ShouldWork() + { + using (var browser = new ChromiumWebBrowser("www.google.com")) + { + browser.RequestContext = new RequestContext(); + + browser.Size = new System.Drawing.Size(1024, 768); + browser.CreateControl(); + + await browser.WaitForInitialLoadAsync(); + var mainFrame = browser.GetMainFrame(); + + Assert.True(mainFrame.IsValid); + Assert.Contains("www.google", mainFrame.Url); + + output.WriteLine("Url {0}", mainFrame.Url); + } + } + + [WinFormsFact] + public async Task ShouldWorkUsingBuilder() + { + using (var browser = new ChromiumWebBrowser("www.google.com")) + { + browser.RequestContext = RequestContext.Configure() + .WithSharedSettings(Cef.GetGlobalRequestContext()) + .Create(); + + browser.Size = new System.Drawing.Size(1024, 768); + browser.CreateControl(); + + await browser.WaitForInitialLoadAsync(); + var mainFrame = browser.GetMainFrame(); + + Assert.True(mainFrame.IsValid); + Assert.Contains("www.google", mainFrame.Url); + + output.WriteLine("Url {0}", mainFrame.Url); + } + } + } +} diff --git a/CefSharp.Test/WinForms/WinFormsBrowserBasicFacts.cs b/CefSharp.Test/WinForms/WinFormsBrowserTests.cs similarity index 66% rename from CefSharp.Test/WinForms/WinFormsBrowserBasicFacts.cs rename to CefSharp.Test/WinForms/WinFormsBrowserTests.cs index f41f034958..08bd376fdb 100644 --- a/CefSharp.Test/WinForms/WinFormsBrowserBasicFacts.cs +++ b/CefSharp.Test/WinForms/WinFormsBrowserTests.cs @@ -13,19 +13,19 @@ namespace CefSharp.Test.WinForms { //NOTE: All Test classes must be part of this collection as it manages the Cef Initialize/Shutdown lifecycle [Collection(CefSharpFixtureCollection.Key)] - public class WinFormsBrowserBasicFacts + public class WinFormsBrowserTests { private readonly ITestOutputHelper output; private readonly CefSharpFixture fixture; - public WinFormsBrowserBasicFacts(ITestOutputHelper output, CefSharpFixture fixture) + public WinFormsBrowserTests(ITestOutputHelper output, CefSharpFixture fixture) { this.fixture = fixture; this.output = output; } [WinFormsFact] - public async Task CanLoadGoogle() + public async Task ShouldWorkWhenLoadingGoogle() { using (var browser = new ChromiumWebBrowser("www.google.com")) { @@ -33,8 +33,8 @@ public async Task CanLoadGoogle() browser.CreateControl(); await browser.WaitForInitialLoadAsync(); - var mainFrame = browser.GetMainFrame(); + Assert.True(mainFrame.IsValid); Assert.Contains("www.google", mainFrame.Url); @@ -43,20 +43,19 @@ public async Task CanLoadGoogle() } [WinFormsFact] - public async Task CanSetBrowserSettingsDisableImageLoadingViaObjectFactory() + public async Task ShouldDisableImageLoadingViaObjectFactory() { using (var browser = new ChromiumWebBrowser("www.google.com")) { var settings = Core.ObjectFactory.CreateBrowserSettings(true); settings.ImageLoading = CefState.Disabled; browser.BrowserSettings = settings; - browser.Size = new System.Drawing.Size(1024, 768); browser.CreateControl(); await browser.WaitForInitialLoadAsync(); - var mainFrame = browser.GetMainFrame(); + Assert.True(mainFrame.IsValid); Assert.Contains("www.google", mainFrame.Url); @@ -65,7 +64,7 @@ public async Task CanSetBrowserSettingsDisableImageLoadingViaObjectFactory() } [WinFormsFact] - public async Task CanSetBrowserSettingsDisableImageLoading() + public async Task ShouldDisableImageLoading() { using (var browser = new ChromiumWebBrowser("www.google.com")) { @@ -73,55 +72,12 @@ public async Task CanSetBrowserSettingsDisableImageLoading() { ImageLoading = CefState.Disabled }; - - browser.Size = new System.Drawing.Size(1024, 768); - browser.CreateControl(); - - await browser.WaitForInitialLoadAsync(); - - var mainFrame = browser.GetMainFrame(); - Assert.True(mainFrame.IsValid); - Assert.Contains("www.google", mainFrame.Url); - - output.WriteLine("Url {0}", mainFrame.Url); - } - } - - [WinFormsFact] - public async Task CanSetRequestContextViaRequestContextBuilder() - { - using (var browser = new ChromiumWebBrowser("www.google.com")) - { - browser.RequestContext = RequestContext.Configure() - .WithSharedSettings(Cef.GetGlobalRequestContext()) - .Create(); - browser.Size = new System.Drawing.Size(1024, 768); browser.CreateControl(); await browser.WaitForInitialLoadAsync(); - var mainFrame = browser.GetMainFrame(); - Assert.True(mainFrame.IsValid); - Assert.Contains("www.google", mainFrame.Url); - - output.WriteLine("Url {0}", mainFrame.Url); - } - } - - [WinFormsFact] - public async Task CanSetRequestContext() - { - using (var browser = new ChromiumWebBrowser("www.google.com")) - { - browser.RequestContext = new RequestContext(); - - browser.Size = new System.Drawing.Size(1024, 768); - browser.CreateControl(); - await browser.WaitForInitialLoadAsync(); - - var mainFrame = browser.GetMainFrame(); Assert.True(mainFrame.IsValid); Assert.Contains("www.google", mainFrame.Url); @@ -140,8 +96,8 @@ public async Task ShouldRespectDisposed() browser.CreateControl(); await browser.WaitForInitialLoadAsync(); - var mainFrame = browser.GetMainFrame(); + Assert.True(mainFrame.IsValid); Assert.Equal(CefExample.DefaultUrl, mainFrame.Url); diff --git a/CefSharp.Test/Wpf.HwndHost/WpfBrowserTests.cs b/CefSharp.Test/Wpf.HwndHost/WpfBrowserTests.cs new file mode 100644 index 0000000000..c5d4561aff --- /dev/null +++ b/CefSharp.Test/Wpf.HwndHost/WpfBrowserTests.cs @@ -0,0 +1,113 @@ +// Copyright © 2017 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Interop; +using CefSharp.Example; +using CefSharp.Wpf.HwndHost; +using Xunit; +using Xunit.Abstractions; + +namespace CefSharp.Test.Wpf.HwndHost +{ + //NOTE: All Test classes must be part of this collection as it manages the Cef Initialize/Shutdown lifecycle + [Collection(CefSharpFixtureCollection.Key)] + [BrowserRefCountDebugging(typeof(ChromiumWebBrowser))] + public class WpfBrowserTests + { + [DllImport("user32.dll")] + private static extern IntPtr SetParent(IntPtr hwnd, IntPtr hwndNewParent); + + private const int HWND_MESSAGE = -3; + + private readonly ITestOutputHelper output; + private readonly CefSharpFixture fixture; + + public WpfBrowserTests(ITestOutputHelper output, CefSharpFixture fixture) + { + this.fixture = fixture; + this.output = output; + } + + [WpfFact] + public async Task ShouldWorkWhenLoadingGoogle() + { + var window = CreateAndShowHiddenWindow(); + + using (var browser = new ChromiumWebBrowser("www.google.com")) + { + window.Content = browser; + + await browser.WaitForInitialLoadAsync(); + var mainFrame = browser.GetMainFrame(); + + Assert.True(mainFrame.IsValid); + Assert.Contains("www.google", mainFrame.Url); + + output.WriteLine("Url {0}", mainFrame.Url); + } + } + + [WpfFact] + public async Task ShouldWorkWhenLoadUrlAsyncImmediately() + { + var window = CreateAndShowHiddenWindow(); + + using (var browser = new ChromiumWebBrowser(string.Empty)) + { + window.Content = browser; + + var response = await browser.LoadUrlAsync("www.google.com"); + var mainFrame = browser.GetMainFrame(); + + Assert.True(response.Success); + Assert.True(mainFrame.IsValid); + Assert.Contains("www.google", mainFrame.Url); + + output.WriteLine("Url {0}", mainFrame.Url); + } + } + + [WpfFact] + public async Task ShouldRespectDisposed() + { + var window = CreateAndShowHiddenWindow(); + + ChromiumWebBrowser browser; + + using (browser = new ChromiumWebBrowser(CefExample.DefaultUrl)) + { + window.Content = browser; + + await browser.WaitForInitialLoadAsync(); + } + + Assert.True(browser.IsDisposed); + + var ex = Assert.Throws(() => + { + browser.Copy(); + }); + } + + private static Window CreateAndShowHiddenWindow() + { + var window = new Window(); + window.Width = 1024; + window.Height = 768; + + var helper = new WindowInteropHelper(window); + + helper.EnsureHandle(); + + SetParent(helper.Handle, (IntPtr)HWND_MESSAGE); + + window.Show(); + return window; + } + } +} diff --git a/CefSharp.Test/Wpf/RequestContextTests.cs b/CefSharp.Test/Wpf/RequestContextTests.cs new file mode 100644 index 0000000000..740c526a7f --- /dev/null +++ b/CefSharp.Test/Wpf/RequestContextTests.cs @@ -0,0 +1,66 @@ +// Copyright © 2017 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System.Threading.Tasks; +using System.Windows; +using CefSharp.Wpf; +using Xunit; +using Xunit.Abstractions; + +namespace CefSharp.Test.Wpf +{ + //NOTE: All Test classes must be part of this collection as it manages the Cef Initialize/Shutdown lifecycle + [Collection(CefSharpFixtureCollection.Key)] + [BrowserRefCountDebugging(typeof(ChromiumWebBrowser))] + public class RequestContextTests + { + private readonly ITestOutputHelper output; + private readonly CefSharpFixture fixture; + + public RequestContextTests(ITestOutputHelper output, CefSharpFixture fixture) + { + this.fixture = fixture; + this.output = output; + } + + [WpfFact] + public async Task ShouldWork() + { + using (var browser = new ChromiumWebBrowser("www.google.com")) + { + browser.RequestContext = new RequestContext(); + browser.CreateBrowser(null, new Size(1024, 786)); + + await browser.WaitForInitialLoadAsync(); + var mainFrame = browser.GetMainFrame(); + + Assert.True(mainFrame.IsValid); + Assert.Contains("www.google", mainFrame.Url); + + output.WriteLine("Url {0}", mainFrame.Url); + } + } + + [WpfFact] + public async Task ShouldWorkUsingBuilder() + { + using (var browser = new ChromiumWebBrowser("www.google.com")) + { + browser.RequestContext = RequestContext.Configure() + .WithSharedSettings(Cef.GetGlobalRequestContext()) + .Create(); + + browser.CreateBrowser(null, new Size(1024, 786)); + + await browser.WaitForInitialLoadAsync(); + var mainFrame = browser.GetMainFrame(); + + Assert.True(mainFrame.IsValid); + Assert.Contains("www.google", mainFrame.Url); + + output.WriteLine("Url {0}", mainFrame.Url); + } + } + } +} diff --git a/CefSharp.Test/Wpf/WaitForRenderIdleTests.cs b/CefSharp.Test/Wpf/WaitForRenderIdleTests.cs index 8ba04724c7..96c20809cb 100644 --- a/CefSharp.Test/Wpf/WaitForRenderIdleTests.cs +++ b/CefSharp.Test/Wpf/WaitForRenderIdleTests.cs @@ -27,8 +27,6 @@ public WaitForRenderIdleTests(ITestOutputHelper output, CefSharpFixture fixture) [WpfFact] public async Task ShouldWork() { - const int expected = 500; - using (var browser = new ChromiumWebBrowser(null, CefExample.DefaultUrl, new Size(1024, 786))) { var start = DateTime.Now; @@ -39,7 +37,7 @@ public async Task ShouldWork() var time = (end - start).TotalMilliseconds; Assert.True(end > start); - Assert.True(time > expected, $"Executed in {time}ms"); + Assert.True(time > 400, $"Executed in {time}ms"); output.WriteLine("Time {0}ms", time); diff --git a/CefSharp.Test/Wpf/WpfBrowserBasicFacts.cs b/CefSharp.Test/Wpf/WpfBrowserTests.cs similarity index 62% rename from CefSharp.Test/Wpf/WpfBrowserBasicFacts.cs rename to CefSharp.Test/Wpf/WpfBrowserTests.cs index 354ccb8443..1fd1a17652 100644 --- a/CefSharp.Test/Wpf/WpfBrowserBasicFacts.cs +++ b/CefSharp.Test/Wpf/WpfBrowserTests.cs @@ -15,25 +15,25 @@ namespace CefSharp.Test.Wpf //NOTE: All Test classes must be part of this collection as it manages the Cef Initialize/Shutdown lifecycle [Collection(CefSharpFixtureCollection.Key)] [BrowserRefCountDebugging(typeof(ChromiumWebBrowser))] - public class WpfBrowserBasicFacts + public class WpfBrowserTests { private readonly ITestOutputHelper output; private readonly CefSharpFixture fixture; - public WpfBrowserBasicFacts(ITestOutputHelper output, CefSharpFixture fixture) + public WpfBrowserTests(ITestOutputHelper output, CefSharpFixture fixture) { this.fixture = fixture; this.output = output; } [WpfFact] - public async Task CanLoadGoogle() + public async Task ShouldWorkWhenLoadingGoogle() { using (var browser = new ChromiumWebBrowser(null, "www.google.com", new Size(1024, 786))) { await browser.WaitForInitialLoadAsync(); - var mainFrame = browser.GetMainFrame(); + Assert.True(mainFrame.IsValid); Assert.Contains("www.google", mainFrame.Url); @@ -42,15 +42,14 @@ public async Task CanLoadGoogle() } [WpfFact] - public async Task CanCallLoadUrlAsyncImmediately() + public async Task ShouldWorkWhenLoadUrlAsyncImmediately() { using (var browser = new ChromiumWebBrowser(null, string.Empty, new Size(1024, 786))) { var response = await browser.LoadUrlAsync("www.google.com"); + var mainFrame = browser.GetMainFrame(); Assert.True(response.Success); - - var mainFrame = browser.GetMainFrame(); Assert.True(mainFrame.IsValid); Assert.Contains("www.google", mainFrame.Url); @@ -59,57 +58,17 @@ public async Task CanCallLoadUrlAsyncImmediately() } [WpfFact] - public async Task CanCallLoadUrlImmediately() + public async Task ShouldWorkWhenLoadUrlImmediately() { using (var browser = new ChromiumWebBrowser()) { - browser.Load("www.google.com"); + browser.LoadUrl("www.google.com"); browser.CreateBrowser(null, new Size(1024, 786)); var response = await browser.WaitForInitialLoadAsync(); - - Assert.True(response.Success); - var mainFrame = browser.GetMainFrame(); - Assert.True(mainFrame.IsValid); - Assert.Contains("www.google", mainFrame.Url); - output.WriteLine("Url {0}", mainFrame.Url); - } - } - - [WpfFact] - public async Task CanSetRequestContext() - { - using (var browser = new ChromiumWebBrowser("www.google.com")) - { - browser.RequestContext = new RequestContext(); - browser.CreateBrowser(null, new Size(1024, 786)); - - await browser.WaitForInitialLoadAsync(); - - var mainFrame = browser.GetMainFrame(); - Assert.True(mainFrame.IsValid); - Assert.Contains("www.google", mainFrame.Url); - - output.WriteLine("Url {0}", mainFrame.Url); - } - } - - [WpfFact] - public async Task CanSetRequestContextViaBuilder() - { - using (var browser = new ChromiumWebBrowser("www.google.com")) - { - browser.RequestContext = RequestContext.Configure() - .WithSharedSettings(Cef.GetGlobalRequestContext()) - .Create(); - - browser.CreateBrowser(null, new Size(1024, 786)); - - await browser.WaitForInitialLoadAsync(); - - var mainFrame = browser.GetMainFrame(); + Assert.True(response.Success); Assert.True(mainFrame.IsValid); Assert.Contains("www.google", mainFrame.Url); diff --git a/CefSharp.WinForms.Example/BrowserForm.Designer.cs b/CefSharp.WinForms.Example/BrowserForm.Designer.cs index e562cc056d..90b0548dc9 100644 --- a/CefSharp.WinForms.Example/BrowserForm.Designer.cs +++ b/CefSharp.WinForms.Example/BrowserForm.Designer.cs @@ -37,6 +37,7 @@ private void InitializeComponent() this.printToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.printToPdfToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.takeScreenShotMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.showDevToolsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.showDevToolsDockedToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.closeDevToolsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -68,10 +69,8 @@ private void InitializeComponent() this.openDataUrlToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.httpbinorgToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.runFileDialogToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.loadExtensionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.javascriptBindingStressTestToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.browserTabControl = new System.Windows.Forms.TabControl(); - this.takeScreenShotMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.menuStrip1.SuspendLayout(); this.SuspendLayout(); // @@ -86,7 +85,7 @@ private void InitializeComponent() this.testToolStripMenuItem}); this.menuStrip1.Location = new System.Drawing.Point(0, 0); this.menuStrip1.Name = "menuStrip1"; - this.menuStrip1.Size = new System.Drawing.Size(730, 24); + this.menuStrip1.Size = new System.Drawing.Size(973, 28); this.menuStrip1.TabIndex = 1; this.menuStrip1.Text = "menuStrip1"; // @@ -106,14 +105,14 @@ private void InitializeComponent() this.toolStripMenuItem3, this.exitToolStripMenuItem}); this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; - this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); + this.fileToolStripMenuItem.Size = new System.Drawing.Size(46, 24); this.fileToolStripMenuItem.Text = "&File"; // // newTabToolStripMenuItem // this.newTabToolStripMenuItem.Name = "newTabToolStripMenuItem"; this.newTabToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.T))); - this.newTabToolStripMenuItem.Size = new System.Drawing.Size(207, 22); + this.newTabToolStripMenuItem.Size = new System.Drawing.Size(262, 26); this.newTabToolStripMenuItem.Text = "&New Tab"; this.newTabToolStripMenuItem.Click += new System.EventHandler(this.NewTabToolStripMenuItemClick); // @@ -121,68 +120,75 @@ private void InitializeComponent() // this.closeTabToolStripMenuItem.Name = "closeTabToolStripMenuItem"; this.closeTabToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.W))); - this.closeTabToolStripMenuItem.Size = new System.Drawing.Size(207, 22); + this.closeTabToolStripMenuItem.Size = new System.Drawing.Size(262, 26); this.closeTabToolStripMenuItem.Text = "&Close Tab"; this.closeTabToolStripMenuItem.Click += new System.EventHandler(this.CloseTabToolStripMenuItemClick); // // hideScrollbarsMenuItem // this.hideScrollbarsMenuItem.Name = "hideScrollbarsMenuItem"; - this.hideScrollbarsMenuItem.Size = new System.Drawing.Size(207, 22); + this.hideScrollbarsMenuItem.Size = new System.Drawing.Size(262, 26); this.hideScrollbarsMenuItem.Text = "Hide Scrollbars"; this.hideScrollbarsMenuItem.Click += new System.EventHandler(this.HideScrollbarsToolStripMenuItemClick); // // printToolStripMenuItem // this.printToolStripMenuItem.Name = "printToolStripMenuItem"; - this.printToolStripMenuItem.Size = new System.Drawing.Size(207, 22); + this.printToolStripMenuItem.Size = new System.Drawing.Size(262, 26); this.printToolStripMenuItem.Text = "&Print"; this.printToolStripMenuItem.Click += new System.EventHandler(this.PrintToolStripMenuItemClick); // // printToPdfToolStripMenuItem // this.printToPdfToolStripMenuItem.Name = "printToPdfToolStripMenuItem"; - this.printToPdfToolStripMenuItem.Size = new System.Drawing.Size(207, 22); + this.printToPdfToolStripMenuItem.Size = new System.Drawing.Size(262, 26); this.printToPdfToolStripMenuItem.Text = "Print To Pdf"; this.printToPdfToolStripMenuItem.Click += new System.EventHandler(this.PrintToPdfToolStripMenuItemClick); // // aboutToolStripMenuItem // this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; - this.aboutToolStripMenuItem.Size = new System.Drawing.Size(207, 22); + this.aboutToolStripMenuItem.Size = new System.Drawing.Size(262, 26); this.aboutToolStripMenuItem.Text = "About"; this.aboutToolStripMenuItem.Click += new System.EventHandler(this.AboutToolStripMenuItemClick); // + // takeScreenShotMenuItem + // + this.takeScreenShotMenuItem.Name = "takeScreenShotMenuItem"; + this.takeScreenShotMenuItem.Size = new System.Drawing.Size(262, 26); + this.takeScreenShotMenuItem.Text = "Take Screenshot"; + this.takeScreenShotMenuItem.Click += new System.EventHandler(this.TakeScreenShotMenuItemClick); + // // showDevToolsMenuItem // this.showDevToolsMenuItem.Name = "showDevToolsMenuItem"; - this.showDevToolsMenuItem.Size = new System.Drawing.Size(207, 22); + this.showDevToolsMenuItem.Size = new System.Drawing.Size(262, 26); this.showDevToolsMenuItem.Text = "Show Dev Tools (Default)"; this.showDevToolsMenuItem.Click += new System.EventHandler(this.ShowDevToolsMenuItemClick); // // showDevToolsDockedToolStripMenuItem // this.showDevToolsDockedToolStripMenuItem.Name = "showDevToolsDockedToolStripMenuItem"; - this.showDevToolsDockedToolStripMenuItem.Size = new System.Drawing.Size(207, 22); + this.showDevToolsDockedToolStripMenuItem.Size = new System.Drawing.Size(262, 26); this.showDevToolsDockedToolStripMenuItem.Text = "Show Dev Tools (Docked)"; this.showDevToolsDockedToolStripMenuItem.Click += new System.EventHandler(this.ShowDevToolsDockedMenuItemClick); // // closeDevToolsMenuItem // this.closeDevToolsMenuItem.Name = "closeDevToolsMenuItem"; - this.closeDevToolsMenuItem.Size = new System.Drawing.Size(207, 22); + this.closeDevToolsMenuItem.Size = new System.Drawing.Size(262, 26); this.closeDevToolsMenuItem.Text = "Close Dev Tools"; this.closeDevToolsMenuItem.Click += new System.EventHandler(this.CloseDevToolsMenuItemClick); // // toolStripMenuItem3 // this.toolStripMenuItem3.Name = "toolStripMenuItem3"; - this.toolStripMenuItem3.Size = new System.Drawing.Size(204, 6); + this.toolStripMenuItem3.Size = new System.Drawing.Size(259, 6); // // exitToolStripMenuItem // this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; - this.exitToolStripMenuItem.Size = new System.Drawing.Size(207, 22); + this.exitToolStripMenuItem.Size = new System.Drawing.Size(262, 26); this.exitToolStripMenuItem.Text = "Exit"; this.exitToolStripMenuItem.Click += new System.EventHandler(this.ExitMenuItemClick); // @@ -201,79 +207,79 @@ private void InitializeComponent() this.toolStripSeparator1, this.copySourceToClipBoardAsyncMenuItem}); this.editToolStripMenuItem.Name = "editToolStripMenuItem"; - this.editToolStripMenuItem.Size = new System.Drawing.Size(39, 20); + this.editToolStripMenuItem.Size = new System.Drawing.Size(49, 24); this.editToolStripMenuItem.Text = "Edit"; // // undoMenuItem // this.undoMenuItem.Name = "undoMenuItem"; - this.undoMenuItem.Size = new System.Drawing.Size(251, 22); + this.undoMenuItem.Size = new System.Drawing.Size(313, 26); this.undoMenuItem.Text = "Undo"; this.undoMenuItem.Click += new System.EventHandler(this.UndoMenuItemClick); // // redoMenuItem // this.redoMenuItem.Name = "redoMenuItem"; - this.redoMenuItem.Size = new System.Drawing.Size(251, 22); + this.redoMenuItem.Size = new System.Drawing.Size(313, 26); this.redoMenuItem.Text = "Redo"; this.redoMenuItem.Click += new System.EventHandler(this.RedoMenuItemClick); // // findMenuItem // this.findMenuItem.Name = "findMenuItem"; - this.findMenuItem.Size = new System.Drawing.Size(251, 22); + this.findMenuItem.Size = new System.Drawing.Size(313, 26); this.findMenuItem.Text = "Find"; this.findMenuItem.Click += new System.EventHandler(this.FindMenuItemClick); // // toolStripMenuItem2 // this.toolStripMenuItem2.Name = "toolStripMenuItem2"; - this.toolStripMenuItem2.Size = new System.Drawing.Size(248, 6); + this.toolStripMenuItem2.Size = new System.Drawing.Size(310, 6); // // cutMenuItem // this.cutMenuItem.Name = "cutMenuItem"; - this.cutMenuItem.Size = new System.Drawing.Size(251, 22); + this.cutMenuItem.Size = new System.Drawing.Size(313, 26); this.cutMenuItem.Text = "Cut"; this.cutMenuItem.Click += new System.EventHandler(this.CutMenuItemClick); // // copyMenuItem // this.copyMenuItem.Name = "copyMenuItem"; - this.copyMenuItem.Size = new System.Drawing.Size(251, 22); + this.copyMenuItem.Size = new System.Drawing.Size(313, 26); this.copyMenuItem.Text = "Copy"; this.copyMenuItem.Click += new System.EventHandler(this.CopyMenuItemClick); // // pasteMenuItem // this.pasteMenuItem.Name = "pasteMenuItem"; - this.pasteMenuItem.Size = new System.Drawing.Size(251, 22); + this.pasteMenuItem.Size = new System.Drawing.Size(313, 26); this.pasteMenuItem.Text = "Paste"; this.pasteMenuItem.Click += new System.EventHandler(this.PasteMenuItemClick); // // deleteMenuItem // this.deleteMenuItem.Name = "deleteMenuItem"; - this.deleteMenuItem.Size = new System.Drawing.Size(251, 22); + this.deleteMenuItem.Size = new System.Drawing.Size(313, 26); this.deleteMenuItem.Text = "Delete"; this.deleteMenuItem.Click += new System.EventHandler(this.DeleteMenuItemClick); // // selectAllMenuItem // this.selectAllMenuItem.Name = "selectAllMenuItem"; - this.selectAllMenuItem.Size = new System.Drawing.Size(251, 22); + this.selectAllMenuItem.Size = new System.Drawing.Size(313, 26); this.selectAllMenuItem.Text = "Select All"; this.selectAllMenuItem.Click += new System.EventHandler(this.SelectAllMenuItemClick); // // toolStripSeparator1 // this.toolStripSeparator1.Name = "toolStripSeparator1"; - this.toolStripSeparator1.Size = new System.Drawing.Size(248, 6); + this.toolStripSeparator1.Size = new System.Drawing.Size(310, 6); // // copySourceToClipBoardAsyncMenuItem // this.copySourceToClipBoardAsyncMenuItem.Name = "copySourceToClipBoardAsyncMenuItem"; - this.copySourceToClipBoardAsyncMenuItem.Size = new System.Drawing.Size(251, 22); + this.copySourceToClipBoardAsyncMenuItem.Size = new System.Drawing.Size(313, 26); this.copySourceToClipBoardAsyncMenuItem.Text = "Copy Source to Clipboard (async)"; this.copySourceToClipBoardAsyncMenuItem.Click += new System.EventHandler(this.CopySourceToClipBoardAsyncClick); // @@ -284,27 +290,27 @@ private void InitializeComponent() this.zoomOutToolStripMenuItem, this.currentZoomLevelToolStripMenuItem}); this.zoomLevelToolStripMenuItem.Name = "zoomLevelToolStripMenuItem"; - this.zoomLevelToolStripMenuItem.Size = new System.Drawing.Size(81, 20); + this.zoomLevelToolStripMenuItem.Size = new System.Drawing.Size(101, 24); this.zoomLevelToolStripMenuItem.Text = "Zoom Level"; // // zoomInToolStripMenuItem // this.zoomInToolStripMenuItem.Name = "zoomInToolStripMenuItem"; - this.zoomInToolStripMenuItem.Size = new System.Drawing.Size(179, 22); + this.zoomInToolStripMenuItem.Size = new System.Drawing.Size(222, 26); this.zoomInToolStripMenuItem.Text = "Zoom In"; this.zoomInToolStripMenuItem.Click += new System.EventHandler(this.ZoomInToolStripMenuItemClick); // // zoomOutToolStripMenuItem // this.zoomOutToolStripMenuItem.Name = "zoomOutToolStripMenuItem"; - this.zoomOutToolStripMenuItem.Size = new System.Drawing.Size(179, 22); + this.zoomOutToolStripMenuItem.Size = new System.Drawing.Size(222, 26); this.zoomOutToolStripMenuItem.Text = "Zoom Out"; this.zoomOutToolStripMenuItem.Click += new System.EventHandler(this.ZoomOutToolStripMenuItemClick); // // currentZoomLevelToolStripMenuItem // this.currentZoomLevelToolStripMenuItem.Name = "currentZoomLevelToolStripMenuItem"; - this.currentZoomLevelToolStripMenuItem.Size = new System.Drawing.Size(179, 22); + this.currentZoomLevelToolStripMenuItem.Size = new System.Drawing.Size(222, 26); this.currentZoomLevelToolStripMenuItem.Text = "Current Zoom Level"; this.currentZoomLevelToolStripMenuItem.Click += new System.EventHandler(this.CurrentZoomLevelToolStripMenuItemClick); // @@ -315,20 +321,20 @@ private void InitializeComponent() this.doesElementWithIDExistToolStripMenuItem, this.listenForButtonClickToolStripMenuItem}); this.scriptToolStripMenuItem.Name = "scriptToolStripMenuItem"; - this.scriptToolStripMenuItem.Size = new System.Drawing.Size(49, 20); + this.scriptToolStripMenuItem.Size = new System.Drawing.Size(61, 24); this.scriptToolStripMenuItem.Text = "Script"; // // isTextInputToolStripMenuItem // this.isTextInputToolStripMenuItem.Name = "isTextInputToolStripMenuItem"; - this.isTextInputToolStripMenuItem.Size = new System.Drawing.Size(272, 22); + this.isTextInputToolStripMenuItem.Size = new System.Drawing.Size(342, 26); this.isTextInputToolStripMenuItem.Text = "Does active element accept text input"; this.isTextInputToolStripMenuItem.Click += new System.EventHandler(this.DoesActiveElementAcceptTextInputToolStripMenuItemClick); // // doesElementWithIDExistToolStripMenuItem // this.doesElementWithIDExistToolStripMenuItem.Name = "doesElementWithIDExistToolStripMenuItem"; - this.doesElementWithIDExistToolStripMenuItem.Size = new System.Drawing.Size(272, 22); + this.doesElementWithIDExistToolStripMenuItem.Size = new System.Drawing.Size(342, 26); this.doesElementWithIDExistToolStripMenuItem.Text = "Does element with ID exist"; this.doesElementWithIDExistToolStripMenuItem.Click += new System.EventHandler(this.DoesElementWithIdExistToolStripMenuItemClick); // @@ -338,20 +344,20 @@ private void InitializeComponent() this.goToDemoPageToolStripMenuItem, this.injectJavascriptCodeToolStripMenuItem}); this.listenForButtonClickToolStripMenuItem.Name = "listenForButtonClickToolStripMenuItem"; - this.listenForButtonClickToolStripMenuItem.Size = new System.Drawing.Size(272, 22); + this.listenForButtonClickToolStripMenuItem.Size = new System.Drawing.Size(342, 26); this.listenForButtonClickToolStripMenuItem.Text = "Listen for button click"; // // goToDemoPageToolStripMenuItem // this.goToDemoPageToolStripMenuItem.Name = "goToDemoPageToolStripMenuItem"; - this.goToDemoPageToolStripMenuItem.Size = new System.Drawing.Size(186, 22); + this.goToDemoPageToolStripMenuItem.Size = new System.Drawing.Size(233, 26); this.goToDemoPageToolStripMenuItem.Text = "Go to demo page"; this.goToDemoPageToolStripMenuItem.Click += new System.EventHandler(this.GoToDemoPageToolStripMenuItemClick); // // injectJavascriptCodeToolStripMenuItem // this.injectJavascriptCodeToolStripMenuItem.Name = "injectJavascriptCodeToolStripMenuItem"; - this.injectJavascriptCodeToolStripMenuItem.Size = new System.Drawing.Size(186, 22); + this.injectJavascriptCodeToolStripMenuItem.Size = new System.Drawing.Size(233, 26); this.injectJavascriptCodeToolStripMenuItem.Text = "Inject Javascript code"; this.injectJavascriptCodeToolStripMenuItem.Click += new System.EventHandler(this.InjectJavascriptCodeToolStripMenuItemClick); // @@ -361,72 +367,59 @@ private void InitializeComponent() this.openDataUrlToolStripMenuItem, this.httpbinorgToolStripMenuItem, this.runFileDialogToolStripMenuItem, - this.loadExtensionsToolStripMenuItem, this.javascriptBindingStressTestToolStripMenuItem}); this.testToolStripMenuItem.Name = "testToolStripMenuItem"; - this.testToolStripMenuItem.Size = new System.Drawing.Size(39, 20); + this.testToolStripMenuItem.Size = new System.Drawing.Size(49, 24); this.testToolStripMenuItem.Text = "Test"; // // openDataUrlToolStripMenuItem // this.openDataUrlToolStripMenuItem.Name = "openDataUrlToolStripMenuItem"; - this.openDataUrlToolStripMenuItem.Size = new System.Drawing.Size(225, 22); + this.openDataUrlToolStripMenuItem.Size = new System.Drawing.Size(283, 26); this.openDataUrlToolStripMenuItem.Text = "Open Data Url"; this.openDataUrlToolStripMenuItem.Click += new System.EventHandler(this.OpenDataUrlToolStripMenuItemClick); // // httpbinorgToolStripMenuItem // this.httpbinorgToolStripMenuItem.Name = "httpbinorgToolStripMenuItem"; - this.httpbinorgToolStripMenuItem.Size = new System.Drawing.Size(225, 22); + this.httpbinorgToolStripMenuItem.Size = new System.Drawing.Size(283, 26); this.httpbinorgToolStripMenuItem.Text = "httpbin.org"; this.httpbinorgToolStripMenuItem.Click += new System.EventHandler(this.OpenHttpBinOrgToolStripMenuItemClick); // // runFileDialogToolStripMenuItem // this.runFileDialogToolStripMenuItem.Name = "runFileDialogToolStripMenuItem"; - this.runFileDialogToolStripMenuItem.Size = new System.Drawing.Size(225, 22); + this.runFileDialogToolStripMenuItem.Size = new System.Drawing.Size(283, 26); this.runFileDialogToolStripMenuItem.Text = "Run File Dialog"; this.runFileDialogToolStripMenuItem.Click += new System.EventHandler(this.RunFileDialogToolStripMenuItemClick); // - // loadExtensionsToolStripMenuItem - // - this.loadExtensionsToolStripMenuItem.Name = "loadExtensionsToolStripMenuItem"; - this.loadExtensionsToolStripMenuItem.Size = new System.Drawing.Size(225, 22); - this.loadExtensionsToolStripMenuItem.Text = "Load Example Extension"; - this.loadExtensionsToolStripMenuItem.Click += new System.EventHandler(this.LoadExtensionsToolStripMenuItemClick); - // // javascriptBindingStressTestToolStripMenuItem // this.javascriptBindingStressTestToolStripMenuItem.Name = "javascriptBindingStressTestToolStripMenuItem"; - this.javascriptBindingStressTestToolStripMenuItem.Size = new System.Drawing.Size(225, 22); + this.javascriptBindingStressTestToolStripMenuItem.Size = new System.Drawing.Size(283, 26); this.javascriptBindingStressTestToolStripMenuItem.Text = "Javascript Binding Stress Test"; this.javascriptBindingStressTestToolStripMenuItem.Click += new System.EventHandler(this.JavascriptBindingStressTestToolStripMenuItemClick); // // browserTabControl // this.browserTabControl.Dock = System.Windows.Forms.DockStyle.Fill; - this.browserTabControl.Location = new System.Drawing.Point(0, 24); + this.browserTabControl.Location = new System.Drawing.Point(0, 28); + this.browserTabControl.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.browserTabControl.Name = "browserTabControl"; this.browserTabControl.SelectedIndex = 0; - this.browserTabControl.Size = new System.Drawing.Size(730, 466); + this.browserTabControl.Size = new System.Drawing.Size(973, 575); this.browserTabControl.TabIndex = 2; // - // takeScreenShotMenuItem - // - this.takeScreenShotMenuItem.Name = "takeScreenShotMenuItem"; - this.takeScreenShotMenuItem.Size = new System.Drawing.Size(207, 22); - this.takeScreenShotMenuItem.Text = "Take Screenshot"; - this.takeScreenShotMenuItem.Click += new System.EventHandler(this.TakeScreenShotMenuItemClick); - // // BrowserForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(730, 490); + this.ClientSize = new System.Drawing.Size(973, 603); this.Controls.Add(this.browserTabControl); this.Controls.Add(this.menuStrip1); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MainMenuStrip = this.menuStrip1; + this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.Name = "BrowserForm"; this.Text = "BrowserForm"; this.menuStrip1.ResumeLayout(false); @@ -476,7 +469,6 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem openDataUrlToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem httpbinorgToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem runFileDialogToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem loadExtensionsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem javascriptBindingStressTestToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem showDevToolsDockedToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem hideScrollbarsMenuItem; diff --git a/CefSharp.WinForms.Example/BrowserForm.cs b/CefSharp.WinForms.Example/BrowserForm.cs index 6e1fae8fac..afb82d6615 100644 --- a/CefSharp.WinForms.Example/BrowserForm.cs +++ b/CefSharp.WinForms.Example/BrowserForm.cs @@ -563,65 +563,6 @@ private void RunFileDialogToolStripMenuItemClick(object sender, EventArgs e) } } - private void LoadExtensionsToolStripMenuItemClick(object sender, EventArgs e) - { - var control = GetCurrentTabControl(); - if (control != null) - { - //The sample extension only works for http(s) schemes - if (control.Browser.GetMainFrame().Url.StartsWith("http")) - { - var requestContext = control.Browser.GetRequestContext(); - - const string cefSharpExampleResourcesFolder = -#if !NETCOREAPP - @"..\..\..\..\CefSharp.Example\Extensions"; -#else - @"..\..\..\..\..\CefSharp.Example\Resources"; -#endif - - var dir = Path.Combine(AppContext.BaseDirectory, cefSharpExampleResourcesFolder); - dir = Path.GetFullPath(dir); - if (!Directory.Exists(dir)) - { - throw new DirectoryNotFoundException("Unable to locate example extensions folder - " + dir); - } - - var extensionHandler = new ExtensionHandler - { - LoadExtensionPopup = (url) => - { - BeginInvoke(new Action(() => - { - var extensionForm = new Form(); - - var extensionBrowser = new ChromiumWebBrowser(url); - //extensionBrowser.IsBrowserInitializedChanged += (s, args) => - //{ - // extensionBrowser.ShowDevTools(); - //}; - - extensionForm.Controls.Add(extensionBrowser); - - extensionForm.Show(this); - })); - }, - GetActiveBrowser = (extension, isIncognito) => - { - //Return the active browser for which the extension will act upon - return control.Browser.BrowserCore; - } - }; - - requestContext.LoadExtensionsFromDirectory(dir, extensionHandler); - } - else - { - MessageBox.Show("The sample extension only works with http(s) schemes, please load a different website and try again", "Unable to load Extension"); - } - } - } - private void JavascriptBindingStressTestToolStripMenuItemClick(object sender, EventArgs e) { var control = GetCurrentTabControl(); diff --git a/CefSharp.WinForms.Example/BrowserTabUserControl.cs b/CefSharp.WinForms.Example/BrowserTabUserControl.cs index 041d08f92c..1aafc64db4 100644 --- a/CefSharp.WinForms.Example/BrowserTabUserControl.cs +++ b/CefSharp.WinForms.Example/BrowserTabUserControl.cs @@ -486,6 +486,11 @@ private void LoadUrl(string url) Browser.LoadUrl(searchUrl); } + //Give the browser focus after starting navigation + if (Browser.CanSelect) + { + Browser.Select(); + } } public async Task HideScrollbarsAsync() diff --git a/CefSharp.WinForms.Example/CefSharp.WinForms.Example.csproj b/CefSharp.WinForms.Example/CefSharp.WinForms.Example.csproj index 8c877b36d0..28b167ba90 100644 --- a/CefSharp.WinForms.Example/CefSharp.WinForms.Example.csproj +++ b/CefSharp.WinForms.Example/CefSharp.WinForms.Example.csproj @@ -9,7 +9,7 @@ app.manifest CefSharp.WinForms.Example.Program false - win7-x86;win7-x64 + win-x86;win-x64 @@ -32,7 +32,11 @@ - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -44,5 +48,5 @@ - +
\ No newline at end of file diff --git a/CefSharp.WinForms.Example/CefSharp.WinForms.Example.netcore.csproj b/CefSharp.WinForms.Example/CefSharp.WinForms.Example.netcore.csproj index ec4060908a..eafb01c1d3 100644 --- a/CefSharp.WinForms.Example/CefSharp.WinForms.Example.netcore.csproj +++ b/CefSharp.WinForms.Example/CefSharp.WinForms.Example.netcore.csproj @@ -7,12 +7,11 @@ bin.netcore\ - + WinExe - netcoreapp3.1;net5.0-windows - $(TargetFrameworks);net6.0-windows + net6.0-windows CefSharp.WinForms.Example CefSharp.WinForms.Example true @@ -23,7 +22,7 @@ arm64 Major @@ -39,7 +38,11 @@ - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -62,6 +65,6 @@ - - + + diff --git a/CefSharp.WinForms.Example/Handlers/WinFormsLifeSpanHandlerEx.cs b/CefSharp.WinForms.Example/Handlers/WinFormsLifeSpanHandlerEx.cs new file mode 100644 index 0000000000..b55e10662b --- /dev/null +++ b/CefSharp.WinForms.Example/Handlers/WinFormsLifeSpanHandlerEx.cs @@ -0,0 +1,176 @@ +// Copyright © 2023 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; +using System.Windows.Forms; +using CefSharp.WinForms.Host; + +namespace CefSharp.WinForms.Example.Handlers +{ + /// + /// A WinForms Specific implementation that demos + /// the process of hosting a Popup using a instance. + /// This implementation returns true in + /// so no WM_CLOSE message is sent, this differs from the default CEF behaviour. + /// + internal class WinFormsLifeSpanHandlerEx : CefSharp.Handler.LifeSpanHandler + { + private Action onPopupBrowserCreated; + private Action onPopupDestroyed; + private Action onPopupCreated; + + /// + /// The delegate will be called when the underlying CEF has been + /// created. The instance is valid until + /// is called. provides low level access to the CEF Browser, you can access frames, view source, + /// perform navigation (via frame) etc. This is equivilent to the . + /// + /// Action to be invoked when the has been created. + /// instance allowing you to chain method calls together + public WinFormsLifeSpanHandlerEx OnPopupBrowserCreated(Action onPopupBrowserCreated) + { + this.onPopupBrowserCreated = onPopupBrowserCreated; + + return this; + } + + /// + /// The will be called when the is to be + /// removed from it's parent. + /// When the is called you must remove/dispose of the . + /// + /// Action to be invoked when the Popup is to be destroyed. + /// instance allowing you to chain method calls together + public WinFormsLifeSpanHandlerEx OnPopupDestroyed(Action onPopupDestroyed) + { + this.onPopupDestroyed = onPopupDestroyed; + + return this; + } + + /// + /// The will be called when the has been + /// created. When the is called you must add the control to it's intended parent. + /// + /// Action to be invoked when the Popup host has been created and is ready to be attached to it's parent. + /// instance allowing you to chain method calls together + public WinFormsLifeSpanHandlerEx OnPopupCreated(Action onPopupCreated) + { + this.onPopupCreated = onPopupCreated; + + return this; + } + + /// + protected override bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser) + { + if (browser.IsPopup) + { + var control = ChromiumHostControlBase.FromBrowser(browser); + + //We don't have a parent control so we allow the default behaviour, required to close + //default popups e.g. DevTools + if (control == null) + { + return false; + } + + //If the main browser is disposed or the handle has been released then we don't + //need to remove the popup (likely removed from menu) + if (!control.IsDisposed && control.IsHandleCreated) + { + try + { + control.BeginInvoke(new Action(() => + { + onPopupDestroyed?.Invoke(control); + + control.Dispose(); + })); + } + catch (ObjectDisposedException) + { + // If the popup is being hosted on a Form that is being + // Closed/Disposed as we attempt to call Control.BeginInvoke + // we can end up with an ObjectDisposedException + // return false (Default behaviour). + return false; + } + } + } + + //No WM_CLOSE message will be sent, manually handle closing + return true; + } + + /// + protected override void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser) + { + if (browser.IsPopup) + { + var webBrowser = (ChromiumWebBrowser)chromiumWebBrowser; + + webBrowser.BeginInvoke((Action) (() => + { + var control = ChromiumHostControlBase.FromBrowser(browser); + + if (control != null) + { + onPopupBrowserCreated?.Invoke(control); + } + })); + + } + } + + /// + protected override void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser) + { + if (!browser.IsDisposed && browser.IsPopup) + { + + } + } + + /// + /// + /// NOTE: DevTools popups DO NOT trigger OnBeforePopup. + /// + protected override bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser) + { + newBrowser = null; + + var webBrowser = (ChromiumWebBrowser)chromiumWebBrowser; + + ChromiumWebBrowser control = null; + + //We need to execute sync here so IWindowInfo.SetAsChild is called before we return false; + webBrowser.Invoke(new Action(() => + { + control = new ChromiumWebBrowser + { + Dock = DockStyle.Fill + }; + + //NOTE: This is important and must be called before the handle is created + control.SetAsPopup(); + control.LifeSpanHandler = this; + + control.CreateControl(); + + var rect = control.ClientRectangle; + + var windowBounds = new CefSharp.Structs.Rect(rect.X, rect.Y, rect.Width, rect.Height); + + windowInfo.SetAsChild(control.Handle, windowBounds); + + onPopupCreated?.Invoke(control, targetUrl, targetFrameName, windowBounds); + })); + + newBrowser = control; + + return false; + } + } +} diff --git a/CefSharp.WinForms.Example/Minimal/SimpleBrowserForm.cs b/CefSharp.WinForms.Example/Minimal/SimpleBrowserForm.cs index 5b6f09cf54..524bb6f6b7 100644 --- a/CefSharp.WinForms.Example/Minimal/SimpleBrowserForm.cs +++ b/CefSharp.WinForms.Example/Minimal/SimpleBrowserForm.cs @@ -177,7 +177,19 @@ private void LoadUrl(string url) { if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) { - browser.Load(url); + browser.LoadUrl(url); + } + else + { + var searchUrl = "https://www.google.com/search?q=" + Uri.EscapeDataString(url); + + browser.LoadUrl(searchUrl); + } + + //Give the browser focus after starting navigation + if (browser.CanSelect) + { + browser.Select(); } } } diff --git a/CefSharp.WinForms.Example/Program.cs b/CefSharp.WinForms.Example/Program.cs index f0d1113afa..67366a48f0 100644 --- a/CefSharp.WinForms.Example/Program.cs +++ b/CefSharp.WinForms.Example/Program.cs @@ -24,17 +24,6 @@ public static int Main(string[] args) if (selfHostSubProcess) { - var processType = CefSharp.Internals.CommandLineArgsParser.GetArgumentValue(args, CefSharp.Internals.CefSharpArguments.SubProcessTypeArgument); - - if (processType == "gpu-process") - { - // Enable DPI Awareness for GPU process. - // Our main application is already DPI aware using WinForms specific features - // **IMPORTANT** There's a mistake in the following doc https://github.com/dotnet/docs-desktop/issues/1485 - // https://docs.microsoft.com/en-us/dotnet/desktop/winforms/high-dpi-support-in-windows-forms - Cef.EnableHighDPISupport(); - } - var exitCode = CefSharp.BrowserSubprocess.SelfHost.Main(args); if (exitCode >= 0) diff --git a/CefSharp.WinForms.Example/app.manifest b/CefSharp.WinForms.Example/app.manifest index 4e0e424b1f..65a6c5f8bd 100644 --- a/CefSharp.WinForms.Example/app.manifest +++ b/CefSharp.WinForms.Example/app.manifest @@ -8,7 +8,7 @@ xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - + @@ -39,13 +39,7 @@ - - - - - - - + diff --git a/CefSharp.WinForms/CefSettings.cs b/CefSharp.WinForms/CefSettings.cs index 050f91b2d4..d04b1ebe9e 100644 --- a/CefSharp.WinForms/CefSettings.cs +++ b/CefSharp.WinForms/CefSettings.cs @@ -10,5 +10,19 @@ namespace CefSharp.WinForms ///
public class CefSettings : CefSettingsBase { + /// + /// Intialize with default values + /// + public CefSettings() : base() + { + // CEF doesn't call GetAuthCredentials unless + // the Chrome login prompt is disabled + // https://github.com/chromiumembedded/cef/issues/3603 + CefCommandLineArgs.Add("disable-chrome-login-prompt"); + + // Disable "Restore pages" popup after incorrect shutdown + // https://github.com/chromiumembedded/cef/issues/3767 + CefCommandLineArgs.Add("hide-crash-restore-bubble"); + } } } diff --git a/CefSharp.WinForms/CefSharp.WinForms.csproj b/CefSharp.WinForms/CefSharp.WinForms.csproj index 2320898019..2364f0b7b2 100644 --- a/CefSharp.WinForms/CefSharp.WinForms.csproj +++ b/CefSharp.WinForms/CefSharp.WinForms.csproj @@ -1,6 +1,6 @@ - net452;net462 + net462 Library true ..\CefSharp.snk @@ -39,6 +39,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + all diff --git a/CefSharp.WinForms/CefSharp.WinForms.netcore.csproj b/CefSharp.WinForms/CefSharp.WinForms.netcore.csproj index bf4c998a14..35e17ccb73 100644 --- a/CefSharp.WinForms/CefSharp.WinForms.netcore.csproj +++ b/CefSharp.WinForms/CefSharp.WinForms.netcore.csproj @@ -7,10 +7,10 @@ bin.netcore\ - + - netcoreapp3.1 + net6.0-windows CefSharp.WinForms CefSharp.WinForms $(BaseOutputPath)$(PlatformTarget)\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml @@ -20,11 +20,12 @@ ..\CefSharp.snk MinimumRecommendedRules.ruleset WINFORMS - 9.0 + 9.0 true + true True embedded @@ -55,8 +56,16 @@ - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; compile + - + \ No newline at end of file diff --git a/CefSharp.WinForms/ChromiumWebBrowser.cs b/CefSharp.WinForms/ChromiumWebBrowser.cs index d39ae42d64..159e9df76a 100644 --- a/CefSharp.WinForms/ChromiumWebBrowser.cs +++ b/CefSharp.WinForms/ChromiumWebBrowser.cs @@ -76,7 +76,7 @@ public partial class ChromiumWebBrowser : ChromiumHostControlBase, IWebBrowserIn /// /// Parking control used to temporarily host the CefBrowser instance - /// when is true. + /// when is true. /// private Control parkingControl; /// @@ -137,7 +137,7 @@ public IBrowserSettings BrowserSettings /// Activates browser upon creation, the default value is false. Prior to version 73 /// the default behaviour was to activate browser on creation (Equivalent of setting this property to true). /// To restore this behaviour set this value to true immediately after you create the instance. - /// https://bitbucket.org/chromiumembedded/cef/issues/1856/branch-2526-cef-activates-browser-window + /// https://github.com/chromiumembedded/cef/issues/1856 /// public bool ActivateBrowserOnCreation { get; set; } /// @@ -333,10 +333,7 @@ private void InitializeFieldsAndCefIfRequired() { if (!initialized) { - if (!Cef.IsInitialized && !Cef.Initialize(new CefSettings())) - { - throw new InvalidOperationException(CefInitializeFailedErrorMessage); - } + InitializeCefInternal(); Cef.AddDisposable(this); @@ -657,12 +654,13 @@ private void OnHandleDestroyedInternal() protected virtual IWindowInfo CreateBrowserWindowInfo(IntPtr handle) { var windowInfo = Core.ObjectFactory.CreateWindowInfo(); + windowInfo.RuntimeStyle = CefSharpSettings.RuntimeStyle ?? CefRuntimeStyle.Alloy; windowInfo.SetAsChild(handle); if (!ActivateBrowserOnCreation) { //Disable Window activation by default - //https://bitbucket.org/chromiumembedded/cef/issues/1856/branch-2526-cef-activates-browser-window + //https://github.com/chromiumembedded/cef/issues/1856/branch-2526-cef-activates-browser-window windowInfo.ExStyle |= WS_EX_NOACTIVATE; } @@ -722,7 +720,7 @@ protected virtual void OnSetBrowserInitialFocus() // It's possible to use Cef.PostAction to invoke directly on the CEF UI Thread, // this also seems to work as expected, using the WinForms UI Thread allows // us to check the Focused property to determine if we actully have focus - // https://bitbucket.org/chromiumembedded/cef/issues/3436/chromium-based-browser-loses-focus-when + // https://github.com/chromiumembedded/cef/issues/3436/chromium-based-browser-loses-focus-when if (InvokeRequired) { BeginInvoke((Action)(() => diff --git a/CefSharp.WinForms/ChromiumWebBrowserDesigner.cs b/CefSharp.WinForms/ChromiumWebBrowserDesigner.cs index 22e0d008fb..1a2966735d 100644 --- a/CefSharp.WinForms/ChromiumWebBrowserDesigner.cs +++ b/CefSharp.WinForms/ChromiumWebBrowserDesigner.cs @@ -4,7 +4,11 @@ using System.Collections; using System.Drawing; +#if NETCOREAPP +using Microsoft.DotNet.DesignTools.Designers; +#else using System.Windows.Forms.Design; +#endif namespace CefSharp.WinForms { diff --git a/CefSharp.WinForms/Handler/LifeSpanHandler.cs b/CefSharp.WinForms/Handler/LifeSpanHandler.cs index 9db4a93bc5..87915658b7 100644 --- a/CefSharp.WinForms/Handler/LifeSpanHandler.cs +++ b/CefSharp.WinForms/Handler/LifeSpanHandler.cs @@ -105,15 +105,26 @@ protected override bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser //need to remove the popup (likely removed from menu) if (!control.IsDisposed && control.IsHandleCreated) { - //We need to invoke in a sync fashion so our IBrowser object is still in scope - //Calling in an async fashion leads to the IBrowser being disposed before we - //can access it. - control.InvokeSyncOnUiThreadIfRequired(new Action(() => + try { - onPopupDestroyed?.Invoke(control, browser); + //We need to invoke in a sync fashion so our IBrowser object is still in scope + //Calling in an async fashion leads to the IBrowser being disposed before we + //can access it. + control.InvokeSyncOnUiThreadIfRequired(new Action(() => + { + onPopupDestroyed?.Invoke(control, browser); - control.Dispose(); - })); + control.Dispose(); + })); + } + catch (ObjectDisposedException) + { + // If the popup is being hosted on a Form that is being + // Closed/Disposed as we attempt to call Control.Invoke + // we can end up with an ObjectDisposedException + // return false (Default behaviour). + return false; + } } } diff --git a/CefSharp.WinForms/Host/ChromiumHostControl.cs b/CefSharp.WinForms/Host/ChromiumHostControl.cs index c5ae0f61a6..a5469b86c8 100644 --- a/CefSharp.WinForms/Host/ChromiumHostControl.cs +++ b/CefSharp.WinForms/Host/ChromiumHostControl.cs @@ -15,7 +15,7 @@ namespace CefSharp.WinForms.Host /// Chromium Browser Host Control, used for hosting Popups in WinForms /// /// - [Docking(DockingBehavior.AutoDock), ToolboxBitmap(typeof(ChromiumHostControl)), + [Docking(DockingBehavior.AutoDock), ToolboxBitmap(typeof(ChromiumWebBrowser)), Designer(typeof(ChromiumWebBrowserDesigner))] public class ChromiumHostControl : ChromiumHostControlBase, IWinFormsChromiumWebBrowser { diff --git a/CefSharp.WinForms/Host/ChromiumHostControlBase.cs b/CefSharp.WinForms/Host/ChromiumHostControlBase.cs index aa08480ee8..8cd2c1e861 100644 --- a/CefSharp.WinForms/Host/ChromiumHostControlBase.cs +++ b/CefSharp.WinForms/Host/ChromiumHostControlBase.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using System.Drawing; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Windows.Forms; namespace CefSharp.WinForms.Host @@ -17,6 +18,10 @@ namespace CefSharp.WinForms.Host /// public abstract class ChromiumHostControlBase : Control { + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + /// /// IntPtr that represents the CefBrowser Hwnd /// Used for sending messages to the browser @@ -121,25 +126,10 @@ protected virtual void ResizeBrowser(int width, int height) { if (BrowserHwnd != IntPtr.Zero) { - ResizeBrowserInternal(Width, Height); + SetWindowPosition(BrowserHwnd, 0, 0, width, height); } } - /// - /// Resizes the browser. - /// - /// width - /// height - /// - /// To avoid the Designer trying to load CefSharp.Core.Runtime we explicitly - /// ask for NoInlining. - /// - [MethodImpl(MethodImplOptions.NoInlining)] - private void ResizeBrowserInternal(int width, int height) - { - NativeMethodWrapper.SetWindowPosition(BrowserHwnd, 0, 0, width, height); - } - /// /// When minimized set the browser window size to 0x0 to reduce resource usage. /// https://github.com/chromiumembedded/cef/blob/c7701b8a6168f105f2c2d6b239ce3958da3e3f13/tests/cefclient/browser/browser_window_std_win.cc#L87 @@ -148,7 +138,7 @@ internal virtual void HideInternal() { if (BrowserHwnd != IntPtr.Zero) { - NativeMethodWrapper.SetWindowPosition(BrowserHwnd, 0, 0, 0, 0); + SetWindowPosition(BrowserHwnd, 0, 0, 0, 0); } } @@ -159,7 +149,7 @@ internal virtual void ShowInternal() { if (BrowserHwnd != IntPtr.Zero) { - NativeMethodWrapper.SetWindowPosition(BrowserHwnd, 0, 0, Width, Height); + SetWindowPosition(BrowserHwnd, 0, 0, Width, Height); } } @@ -183,6 +173,27 @@ internal void RaiseIsBrowserInitializedChangedEvent() IsBrowserInitializedChanged?.Invoke(this, EventArgs.Empty); } + private void SetWindowPosition(IntPtr handle, int x, int y, int width, int height) + { + const uint SWP_NOMOVE = 0x0002; + const uint SWP_NOZORDER = 0x0004; + const uint SWP_NOACTIVATE = 0x0010; + + if (handle != IntPtr.Zero) + { + if (width == 0 && height == 0) + { + // For windowed browsers when the frame window is minimized set the + // browser window size to 0x0 to reduce resource usage. + SetWindowPos(handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); + } + else + { + SetWindowPos(handle, IntPtr.Zero, x, y, width, height, SWP_NOZORDER); + } + } + } + /// /// Gets the or associated with /// a specific instance. diff --git a/CefSharp.WinForms/IWinFormsChromiumWebBrowser.cs b/CefSharp.WinForms/IWinFormsChromiumWebBrowser.cs index 7a145f8a08..f98142c4ed 100644 --- a/CefSharp.WinForms/IWinFormsChromiumWebBrowser.cs +++ b/CefSharp.WinForms/IWinFormsChromiumWebBrowser.cs @@ -38,5 +38,11 @@ public interface IWinFormsChromiumWebBrowser : IChromiumWebBrowserBase, IWin32Wi /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread. /// event EventHandler IsBrowserInitializedChanged; + + /// + bool CanSelect { get; } + + /// + void Select(); } } diff --git a/CefSharp.Wpf.Example/App.xaml b/CefSharp.Wpf.Example/App.xaml index b1ea6e7419..47e4e21753 100644 --- a/CefSharp.Wpf.Example/App.xaml +++ b/CefSharp.Wpf.Example/App.xaml @@ -1,55 +1,62 @@ - - + + + + + - --> + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + diff --git a/CefSharp.Wpf.Example/CefSharp.Wpf.Example.csproj b/CefSharp.Wpf.Example/CefSharp.Wpf.Example.csproj index f6aef22f8e..343954c092 100644 --- a/CefSharp.Wpf.Example/CefSharp.Wpf.Example.csproj +++ b/CefSharp.Wpf.Example/CefSharp.Wpf.Example.csproj @@ -9,7 +9,7 @@ app.manifest CefSharp.Wpf.Example.Program false - win7-x86;win7-x64 + win-x86;win-x64 @@ -31,7 +31,12 @@ - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -43,5 +48,5 @@ - + \ No newline at end of file diff --git a/CefSharp.Wpf.Example/CefSharp.Wpf.Example.netcore.csproj b/CefSharp.Wpf.Example/CefSharp.Wpf.Example.netcore.csproj index 5abe63d235..f375c1dfad 100644 --- a/CefSharp.Wpf.Example/CefSharp.Wpf.Example.netcore.csproj +++ b/CefSharp.Wpf.Example/CefSharp.Wpf.Example.netcore.csproj @@ -7,11 +7,11 @@ bin.netcore\ - + WinExe - netcoreapp3.1 + net6.0-windows CefSharp.Wpf.Example CefSharp.Wpf.Example true @@ -23,7 +23,7 @@ arm64 CefSharp.Wpf.Example.Program Major @@ -39,7 +39,13 @@ - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + @@ -55,6 +61,6 @@ - - + + diff --git a/CefSharp.Wpf.Example/MainWindow.xaml b/CefSharp.Wpf.Example/MainWindow.xaml index 39a2129a7f..bf607e2286 100644 --- a/CefSharp.Wpf.Example/MainWindow.xaml +++ b/CefSharp.Wpf.Example/MainWindow.xaml @@ -1,8 +1,16 @@ @@ -10,7 +18,7 @@ - + @@ -47,7 +55,6 @@ - @@ -63,12 +70,10 @@ - - - - - - - - + + private bool initialFocus; + /// + /// The class that coordinates the positioning of the dropdown if wanted. + /// + internal IMousePositionTransform MousePositionTransform { get; set; } + /// /// When enabled the browser will resize by 1px when it becomes visible to workaround /// the upstream issue - /// Hack to work around upstream issue https://bitbucket.org/chromiumembedded/cef/issues/3427/osr-rendering-bug-when-minimizing-and + /// Hack to work around upstream issue https://github.com/chromiumembedded/cef/issues/3427 /// Disabled by default /// public bool ResizeHackEnabled { get; set; } = false; @@ -162,7 +168,7 @@ public partial class ChromiumWebBrowser : Control, IRenderWebBrowser, IWpfWebBro /// Number of milliseconds to wait after resizing the browser when it first /// becomes visible. After the delay the browser will revert to it's /// original size. - /// Hack to workaround upstream issue https://bitbucket.org/chromiumembedded/cef/issues/3427/osr-rendering-bug-when-minimizing-and + /// Hack to workaround upstream issue https://github.com/chromiumembedded/cef/issues/3427 /// public int ResizeHackDelayInMs { get; set; } = 50; @@ -546,16 +552,7 @@ public ChromiumWebBrowser(string initialAddress) [MethodImpl(MethodImplOptions.NoInlining)] private void NoInliningConstructor() { - //Initialize CEF if it hasn't already been initialized - if (!Cef.IsInitialized) - { - var settings = new CefSettings(); - - if (!Cef.Initialize(settings)) - { - throw new InvalidOperationException(CefInitializeFailedErrorMessage); - } - } + InitializeCefInternal(); //Add this ChromiumWebBrowser instance to a list of IDisposable objects // that if still alive at the time Cef.Shutdown is called will be disposed of @@ -608,11 +605,17 @@ private void NoInliningConstructor() browserSettings = Core.ObjectFactory.CreateBrowserSettings(autoDispose: true); - WpfKeyboardHandler = new WpfKeyboardHandler(this); + SetupWpfKeyboardHandler(); + + if (WpfKeyboardHandler == null) + { + WpfKeyboardHandler = new WpfKeyboardHandler(this); + } PresentationSource.AddSourceChangedHandler(this, PresentationSourceChangedHandler); MenuHandler = new ContextMenuHandler(); + MousePositionTransform = new NoOpMousePositionTransform(); UseLayoutRounding = true; } @@ -673,7 +676,10 @@ private void InternalDispose(bool disposing) //Stop rendering immediately so later on when we dispose of the //RenderHandler no further OnPaint calls take place //Check browser not null as it's possible to call Dispose before it's created - browser?.GetHost().WasHidden(true); + if (browser?.IsDisposed == false) + { + browser?.GetHost().WasHidden(true); + } UiThreadRunAsync(() => { @@ -753,6 +759,8 @@ private void InternalDispose(bool disposing) // is called. LifeSpanHandler = null; + UnsubscribeInputLanguageChanged(); + WpfKeyboardHandler?.Dispose(); WpfKeyboardHandler = null; @@ -769,6 +777,44 @@ private void InternalDispose(bool disposing) Cef.RemoveDisposable(this); } + /// + /// Setup the used by this instance. + /// There are two main keyboard handler implementations currently + /// and . If + /// returns true for the current + /// then the instance will be used, otherwise the older + /// instance will be used. + /// Override to provide your own custom keyboard handler. + /// + protected virtual void SetupWpfKeyboardHandler() + { + try + { + var inputLang = InputLanguageManager.Current.CurrentInputLanguage; + + var useImeKeyboardHandler = WpfImeKeyboardHandler.UseImeKeyboardHandler(inputLang.KeyboardLayoutId); + + if (useImeKeyboardHandler) + { + WpfKeyboardHandler = new WpfImeKeyboardHandler(this); + } + else + { + InputLanguageManager.Current.InputLanguageChanged += OnInputLanguageChanged; + + WpfKeyboardHandler = new WpfKeyboardHandler(this); + } + } + catch (Exception ex) + { + //For now we'll ignore any errors + Trace.TraceError($"Error initializing keyboard handler: {ex.ToString()}"); + + // For now we'll ignore any errors and just use the default keyboard handler + WpfKeyboardHandler = new WpfKeyboardHandler(this); + } + } + /// /// Gets the ScreenInfo - currently used to get the DPI scale factor. /// @@ -913,15 +959,17 @@ protected virtual bool StartDragging(IDragData dragData, DragOperationsMask allo { if (browser != null) { - //DoDragDrop will fire DragEnter event + // DoDragDrop will fire DragEnter event var result = DragDrop.DoDragDrop(this, dataObject, allowedOps.GetDragEffects()); + var dragOperationMask = GetDragOperationsMask(result, currentDragDropEffects); - //DragData was stored so when DoDragDrop fires DragEnter we reuse a clone of the IDragData provided here + // DragData was stored so when DoDragDrop fires DragEnter we reuse a clone of the IDragData provided here currentDragData = null; + currentDragDropEffects = null; - //If result == DragDropEffects.None then we'll send DragOperationsMask.None - //effectively cancelling the drag operation - browser.GetHost().DragSourceEndedAt(x, y, result.GetDragOperationsMask()); + // If result or the last recorded drag drop effect were DragDropEffects.None + // then we'll send DragOperationsMask.None, effectively cancelling the drag operation + browser.GetHost().DragSourceEndedAt(x, y, dragOperationMask); browser.GetHost().DragSourceSystemDragEnded(); } }); @@ -929,6 +977,22 @@ protected virtual bool StartDragging(IDragData dragData, DragOperationsMask allo return true; } + protected virtual DragOperationsMask GetDragOperationsMask(DragDropEffects result, DragDropEffects? dragEffects) + { + // Ensure the drag and drop operation wasn't cancelled + var finalEffectMask = (dragEffects ?? DragDropEffects.None).GetDragOperationsMask() & result.GetDragOperationsMask(); + + // We shouldn't have an instance where finalEffectMask signfies multiple effects, as the operation + // set by UpdateDragCursor should only ever be none, move, copy, or link. However, if it does, we'll + // just default to copy, as that reflects the defaulting behavior of GetDragEffects. + if (finalEffectMask.HasFlag(DragOperationsMask.Every)) + { + return DragOperationsMask.Copy; + } + + return finalEffectMask; + } + /// void IRenderWebBrowser.UpdateDragCursor(DragOperationsMask operation) { @@ -945,21 +1009,30 @@ protected virtual void UpdateDragCursor(DragOperationsMask operation) } /// - void IRenderWebBrowser.OnAcceleratedPaint(PaintElementType type, Rect dirtyRect, IntPtr sharedHandle) + void IRenderWebBrowser.OnAcceleratedPaint(PaintElementType type, Rect dirtyRect, AcceleratedPaintInfo acceleratedPaintInfo) { - OnAcceleratedPaint(type == PaintElementType.Popup, dirtyRect, sharedHandle); + OnAcceleratedPaint(type == PaintElementType.Popup, dirtyRect, acceleratedPaintInfo); } /// /// Called when an element has been rendered to the shared texture handle. /// This method is only called when is set to true + /// + /// The underlying implementation uses a pool to deliver frames. As a result, + /// the handle may differ every frame depending on how many frames are + /// in-progress. The handle's resource cannot be cached and cannot be accessed + /// outside of this callback. It should be reopened each time this callback is + /// executed and the contents should be copied to a texture owned by the + /// client application. The contents of acceleratedPaintInfo + /// will be released back to the pool after this callback returns. /// /// indicates whether the element is the view or the popup widget. /// contains the set of rectangles in pixel coordinates that need to be repainted - /// is the handle for a D3D11 Texture2D that can be accessed via ID3D11Device using the OpenSharedResource method. - protected virtual void OnAcceleratedPaint(bool isPopup, Rect dirtyRect, IntPtr sharedHandle) + /// contains the shared handle; on Windows it is a + /// HANDLE to a texture that can be opened with D3D11 OpenSharedResource. + protected virtual void OnAcceleratedPaint(bool isPopup, Rect dirtyRect, AcceleratedPaintInfo acceleratedPaintInfo) { - RenderHandler?.OnAcceleratedPaint(isPopup, dirtyRect, sharedHandle); + RenderHandler?.OnAcceleratedPaint(isPopup, dirtyRect, acceleratedPaintInfo); } /// @@ -1037,7 +1110,11 @@ void IRenderWebBrowser.OnPopupShow(bool isOpen) /// if set to true [is open]. protected virtual void OnPopupShow(bool isOpen) { - UiThreadRunAsync(() => { popupImage.Visibility = isOpen ? Visibility.Visible : Visibility.Hidden; }); + UiThreadRunAsync(() => + { + popupImage.Visibility = isOpen ? Visibility.Visible : Visibility.Hidden; + MousePositionTransform.OnPopupShow(isOpen); + }); } /// @@ -1603,16 +1680,18 @@ private static void OnTooltipTextChanged(DependencyObject d, DependencyPropertyC /// new value protected virtual void OnTooltipTextChanged(string oldValue, string newValue) { - var timer = tooltipTimer; - if (timer == null) + // There are cases where oldValue is null and newValue is string.Empty + // and vice versa, simply ignore when string.IsNullOrEmpty for both. + if (string.IsNullOrEmpty(oldValue) && string.IsNullOrEmpty(newValue)) { return; } - // There are cases where oldValue is null and newValue is string.Empty - // and vice versa, simply ignore when string.IsNullOrEmpty for both. - if (string.IsNullOrEmpty(oldValue) && string.IsNullOrEmpty(newValue)) + var timer = tooltipTimer; + if (timer == null) { + OpenOrCloseToolTip(newValue); + return; } @@ -1664,7 +1743,6 @@ private void OnDrop(object sender, DragEventArgs e) { var mouseEvent = GetMouseEvent(e); var effect = e.AllowedEffects.GetDragOperationsMask(); - browser.GetHost().DragTargetDragOver(mouseEvent, effect); browser.GetHost().DragTargetDragDrop(mouseEvent); } @@ -1694,7 +1772,7 @@ private void OnDragOver(object sender, DragEventArgs e) { browser.GetHost().DragTargetDragOver(GetMouseEvent(e), e.AllowedEffects.GetDragOperationsMask()); } - e.Effects = currentDragDropEffects; + e.Effects = currentDragDropEffects ?? DragDropEffects.None; e.Handled = true; } @@ -1712,6 +1790,7 @@ private void OnDragEnter(object sender, DragEventArgs e) //DoDragDrop will fire this handler for internally sourced Drag/Drop operations //we use the existing IDragData (cloned copy) + var dragData = currentDragData ?? e.GetDragData(); browser.GetHost().DragTargetDragEnter(dragData, mouseEvent, effect); @@ -1719,6 +1798,26 @@ private void OnDragEnter(object sender, DragEventArgs e) } } + private void OnInputLanguageChanged(object sender, InputLanguageEventArgs e) + { + // If we are already using the WpfImeKeyboardHandler then we'll ignore any changes + if (WpfKeyboardHandler?.GetType() == typeof(WpfImeKeyboardHandler)) + { + return; + } + + var useImeKeyboardHandler = WpfImeKeyboardHandler.UseImeKeyboardHandler(e.NewLanguage.KeyboardLayoutId); + + if (useImeKeyboardHandler) + { + var oldKeyboardHandler = WpfKeyboardHandler; + WpfKeyboardHandler = new WpfImeKeyboardHandler(this); + oldKeyboardHandler?.Dispose(); + + InputLanguageManager.Current.InputLanguageChanged -= OnInputLanguageChanged; + } + } + /// /// PresentationSource changed handler. /// @@ -1747,10 +1846,10 @@ private void PresentationSourceChangedHandler(object sender, SourceChangedEventA { CleanupElement = window; } - else if(CleanupElement is Window parent) + else if (CleanupElement is Window parent) { - //If the CleanupElement is a window then move it to the new Window - if(parent != window) + // If the CleanupElement is a window then move it to the new Window + if (parent != window) { CleanupElement = window; } @@ -1776,7 +1875,6 @@ private void PresentationSourceChangedHandler(object sender, SourceChangedEventA } } -#if NETCOREAPP || NET462 /// protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi) { @@ -1784,7 +1882,6 @@ protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi) base.OnDpiChanged(oldDpi, newDpi); } -#endif private void OnWindowStateChanged(object sender, EventArgs e) { @@ -2056,14 +2153,18 @@ private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArg /// The instance containing the event data. private void OnLoaded(object sender, RoutedEventArgs routedEventArgs) { - // TODO: Consider making the delay here configurable. - tooltipTimer = new DispatcherTimer( - TimeSpan.FromSeconds(0.5), - DispatcherPriority.Render, - OnTooltipTimerTick, - Dispatcher - ); - tooltipTimer.IsEnabled = false; + var initialShowDelay = ToolTipService.GetInitialShowDelay(this); + + if (initialShowDelay > 0) + { + tooltipTimer = new DispatcherTimer( + TimeSpan.FromMilliseconds(initialShowDelay), + DispatcherPriority.Render, + OnTooltipTimerTick, + Dispatcher + ); + tooltipTimer.IsEnabled = false; + } //Initial value for screen location browserScreenLocation = GetBrowserScreenLocation(); @@ -2110,8 +2211,10 @@ private void SetPopupSizeAndPositionImpl(Rect rect) popupImage.Width = rect.Width; popupImage.Height = rect.Height; - Canvas.SetLeft(popupImage, rect.X); - Canvas.SetTop(popupImage, rect.Y); + var point = MousePositionTransform.UpdatePopupSizeAndPosition(rect, viewRect); + + Canvas.SetLeft(popupImage, point.X); + Canvas.SetTop(popupImage, point.Y); } /// @@ -2278,6 +2381,7 @@ protected override void OnMouseMove(MouseEventArgs e) var point = e.GetPosition(this); var modifiers = e.GetModifiers(); + MousePositionTransform.TransformMousePoint(ref point); browser.GetHost().SendMouseMoveEvent((int)point.X, (int)point.Y, false, modifiers); } @@ -2295,12 +2399,11 @@ protected override void OnMouseWheel(MouseWheelEventArgs e) var point = e.GetPosition(this); var modifiers = e.GetModifiers(); var isShiftKeyDown = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift); - var pointX = (int)point.X; - var pointY = (int)point.Y; + MousePositionTransform.TransformMousePoint(ref point); browser.SendMouseWheelEvent( - pointX, - pointY, + (int)point.X, + (int)point.Y, deltaX: isShiftKeyDown ? e.Delta : 0, deltaY: !isShiftKeyDown ? e.Delta : 0, modifiers: modifiers); @@ -2446,11 +2549,12 @@ private void OnMouseButton(MouseButtonEventArgs e) //Anything greater than 3 then we send click count of 1 var clickCount = e.ClickCount; - if(clickCount > 3) + if (clickCount > 3) { clickCount = 1; } + MousePositionTransform.TransformMousePoint(ref point); browser.GetHost().SendMouseClickEvent((int)point.X, (int)point.Y, (MouseButtonType)e.ChangedButton, mouseUp, clickCount, modifiers); } @@ -2560,7 +2664,7 @@ protected override AutomationPeer OnCreateAutomationPeer() /// public void Load(string url) { - if(IsDisposed) + if (IsDisposed) { return; } @@ -2638,8 +2742,6 @@ private void ZoomReset() /// correspond to 96, 120, 144, 192 DPI (referred to as 100%, 125%, 150%, 200% in the Windows GUI). /// /// new DPI - /// .Net 4.6.2 adds HwndSource.DpiChanged which could be used to automatically - /// handle DPI change, unfortunately we still target .Net 4.5.2 public virtual void NotifyDpiChange(float newDpi) { //Do nothing @@ -2680,7 +2782,6 @@ public virtual void NotifyDpiChange(float newDpi) } } -#if NETCOREAPP || NET462 /// /// Waits for the page rendering to be idle for . /// Rendering is considered to be idle when no events have occured @@ -2716,8 +2817,21 @@ public async Task WaitForRenderIdleAsync(int idleTimeInMs = 500, TimeSpan? timeo //Every time Paint is called we reset our timer handler = (s, args) => { - idleTimer.Stop(); - idleTimer.Start(); + try + { + idleTimer?.Stop(); + idleTimer?.Start(); + } + catch (ObjectDisposedException) + { + // NOTE: When the Elapsed (or Timeout) and Paint are fire at almost exactly + // the same time, the timer maybe Disposed on a different thread. + // https://github.com/cefsharp/CefSharp/issues/4597 + } + catch (Exception ex) + { + renderIdleTcs.TrySetException(ex); + } }; idleTimer.Start(); @@ -2738,7 +2852,6 @@ public async Task WaitForRenderIdleAsync(int idleTimeInMs = 500, TimeSpan? timeo throw; } } -#endif /// /// Legacy keyboard handler uses WindowProc callback interceptor to forward keypress events @@ -2773,7 +2886,7 @@ public IBrowser GetBrowser() return browser; } - + private async Task CefUiThreadRunAsync(Action action) { if (!IsDisposed && InternalIsBrowserInitialized()) @@ -2793,7 +2906,7 @@ await Cef.UIThreadTaskFactory.StartNew(delegate } /// - /// Resize hack for https://bitbucket.org/chromiumembedded/cef/issues/3427/osr-rendering-bug-when-minimizing-and + /// Resize hack for https://github.com/chromiumembedded/cef/issues/3427/osr-rendering-bug-when-minimizing-and /// /// Task private async Task ResizeHackRun() @@ -2817,5 +2930,35 @@ private async Task ResizeHackRun() } } } + + private void UnsubscribeInputLanguageChanged() + { + // If we are using WpfImeKeyboardHandler then we + // shouldn't need to unsubsribe from the handler + if (WpfKeyboardHandler?.GetType() == typeof(WpfImeKeyboardHandler)) + { + return; + } + + try + { + // Dispose can be called on non UI thread, InputLanguageManager.Current + // is thread specific + UiThreadRunAsync(() => + { + var inputLangManager = InputLanguageManager.Current; + + if (inputLangManager != null) + { + inputLangManager.InputLanguageChanged -= OnInputLanguageChanged; + } + }); + } + catch (Exception ex) + { + //For now we'll ignore any errors + Trace.TraceError($"Error unsubscribing from InputLanguageChanged {ex.ToString()}"); + } + } } } diff --git a/CefSharp.Wpf/Experimental/ExperimentalExtensions.cs b/CefSharp.Wpf/Experimental/ExperimentalExtensions.cs new file mode 100644 index 0000000000..539ba6dde4 --- /dev/null +++ b/CefSharp.Wpf/Experimental/ExperimentalExtensions.cs @@ -0,0 +1,35 @@ +using CefSharp.Wpf.Internals; + +namespace CefSharp.Wpf.Experimental +{ + /// + /// Experimental Extensions + /// + public static class ExperimentalExtensions + { + /// + /// Html dropdown goes off screen when near bottom of page by default + /// Calling this method to use the implementation + /// to reopsition Popups and mouse. + /// + /// Issue https://github.com/cefsharp/CefSharp/issues/2820 + /// + /// browser + public static void UsePopupMouseTransform(this ChromiumWebBrowser chromiumWebBrowser) + { + chromiumWebBrowser.MousePositionTransform = new MousePositionTransform(); + } + + /// + /// Use a custom implemntation + /// + /// browser + /// custom implementation of + /// or defaults to if null. + /// + public static void UsePopupMouseTransform(this ChromiumWebBrowser chromiumWebBrowser, IMousePositionTransform mousePositionTransform) + { + chromiumWebBrowser.MousePositionTransform = mousePositionTransform ?? new NoOpMousePositionTransform(); + } + } +} diff --git a/CefSharp.Wpf/Experimental/LifespanHandler.cs b/CefSharp.Wpf/Experimental/LifespanHandler.cs index a9fc37b720..2bc6b36e02 100644 --- a/CefSharp.Wpf/Experimental/LifespanHandler.cs +++ b/CefSharp.Wpf/Experimental/LifespanHandler.cs @@ -76,7 +76,7 @@ public class LifeSpanHandler : CefSharp.Handler.LifeSpanHandler /// /// Default constructor /// - /// optional delegate to create a custom + /// optional delegate to create a custom instance. public LifeSpanHandler(LifeSpanHandlerCreatePopupChromiumWebBrowser chromiumWebBrowserCreatedDelegate = null) { this.chromiumWebBrowserCreatedDelegate = chromiumWebBrowserCreatedDelegate; diff --git a/CefSharp.Wpf/Experimental/WpfIMEKeyboardHandler.cs b/CefSharp.Wpf/Experimental/WpfIMEKeyboardHandler.cs index 14740d50a2..c92a382b66 100644 --- a/CefSharp.Wpf/Experimental/WpfIMEKeyboardHandler.cs +++ b/CefSharp.Wpf/Experimental/WpfIMEKeyboardHandler.cs @@ -10,6 +10,7 @@ using CefSharp.Internals; using CefSharp.Structs; using CefSharp.Wpf.Internals; +using System.Windows.Media; using Point = System.Windows.Point; using Range = CefSharp.Structs.Range; using Rect = CefSharp.Structs.Rect; @@ -38,8 +39,8 @@ public class WpfImeKeyboardHandler : WpfKeyboardHandler /// The owner. public WpfImeKeyboardHandler(ChromiumWebBrowser owner) : base(owner) { - } - + } + /// /// Change composition range. /// @@ -60,8 +61,15 @@ public void ChangeCompositionRange(Range selectionRange, Rect[] characterBounds) owner.UiThreadRunAsync(() => { //TODO: Getting the root window for every composition range change seems expensive, - //we should cache the position and update it on window move. - var parentWindow = Window.GetWindow(owner); + //we should cache the position and update it on window move. + var parentWindow = (FrameworkElement)Window.GetWindow(owner); + + //In Winform embedded wpf borwser mode, Window.GetWindow(owner) is null, so use a custom function to get the outermost visual element. + if(parentWindow == null) + { + parentWindow = GetOutermostElement(owner); + } + if (parentWindow != null) { //TODO: What are we calculating here exactly??? @@ -286,6 +294,7 @@ private void OnImeComposition(IBrowserHost browserHost, IntPtr hwnd, int lParam) /// /// Cancel composition. /// + /// browser host /// The hwnd. private void CancelComposition(IBrowserHost browserHost, IntPtr hwnd) { @@ -345,7 +354,7 @@ private void CreateImeWindow(IntPtr hwnd) } } - private int PrimaryLangId(int lgid) + private static int PrimaryLangId(int lgid) { return lgid & 0x3ff; } @@ -439,5 +448,44 @@ private void UpdateCaretPosition(int index) { MoveImeWindow(source.Handle); } + + /// + /// Get the outermost element of the browser + /// + /// The browser + /// The outermost element + private FrameworkElement GetOutermostElement(FrameworkElement control) + { + DependencyObject parent = VisualTreeHelper.GetParent(control); + DependencyObject current = control; + + while (parent != null) + { + current = parent; + parent = VisualTreeHelper.GetParent(current); + } + + return current as FrameworkElement; + } + + /// + /// Based on the determine if we + /// should be using IME. + /// + /// Keyboard Layout Id (obtained from ) + /// + /// returns true if the keyboard layout matches one of our listed that support IME, otherwise false. + /// + public static bool UseImeKeyboardHandler(int keyboardLayoutId) + { + var langId = PrimaryLangId(keyboardLayoutId); + + if (langId == ImeNative.LANG_KOREAN || langId == ImeNative.LANG_JAPANESE || langId == ImeNative.LANG_CHINESE) + { + return true; + } + + return false; + } } } diff --git a/CefSharp.Wpf/Handler/ContextMenuHandler.cs b/CefSharp.Wpf/Handler/ContextMenuHandler.cs index 57da637aa7..2bf9c4728d 100644 --- a/CefSharp.Wpf/Handler/ContextMenuHandler.cs +++ b/CefSharp.Wpf/Handler/ContextMenuHandler.cs @@ -6,6 +6,7 @@ using System.Windows.Controls.Primitives; using System.Windows.Controls; using System.Windows; +using CefSharp.Wpf.Internals; namespace CefSharp.Wpf.Handler { diff --git a/CefSharp.Wpf/HwndHost/CefSettings.cs b/CefSharp.Wpf/HwndHost/CefSettings.cs new file mode 100644 index 0000000000..01b1d0d551 --- /dev/null +++ b/CefSharp.Wpf/HwndHost/CefSettings.cs @@ -0,0 +1,26 @@ +// Copyright © 2019 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +namespace CefSharp.Wpf.HwndHost +{ + /// + /// Initialization settings. Many of these and other settings can also configured using command-line switches. + /// + public class CefSettings : CefSettingsBase + { + /// + /// Intialize with default values + /// + public CefSettings() : base() + { + // CEF doesn't call GetAuthCredentials unless the Chrome login prompt is disabled + // https://github.com/chromiumembedded/cef/issues/3603 + CefCommandLineArgs.Add("disable-chrome-login-prompt"); + + // Disable "Restore pages" popup after incorrect shutdown + // https://github.com/chromiumembedded/cef/issues/3767 + CefCommandLineArgs.Add("hide-crash-restore-bubble"); + } + } +} diff --git a/CefSharp.Wpf/HwndHost/ChromiumWebBrowser.cs b/CefSharp.Wpf/HwndHost/ChromiumWebBrowser.cs new file mode 100644 index 0000000000..36143c170c --- /dev/null +++ b/CefSharp.Wpf/HwndHost/ChromiumWebBrowser.cs @@ -0,0 +1,1972 @@ +// Copyright © 2019 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Threading; +using CefSharp.DevTools.Page; +using CefSharp.Internals; +using CefSharp.Structs; +using CefSharp.Wpf.HwndHost.Internals; +using CefSharp.Wpf.Internals; + +namespace CefSharp.Wpf.HwndHost +{ + /// + /// ChromiumWebBrowser is the WPF web browser control + /// + /// + /// + /// based on https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/walkthrough-hosting-a-win32-control-in-wpf + /// and https://stackoverflow.com/questions/6500336/custom-dwm-drawn-window-frame-flickers-on-resizing-if-the-window-contains-a-hwnd/17471534#17471534 + public class ChromiumWebBrowser : System.Windows.Interop.HwndHost, IWebBrowserInternal, IWpfWebBrowser + { + public const string BrowserNotInitializedExceptionErrorMessage = + "The ChromiumWebBrowser instance creates the underlying Chromium Embedded Framework (CEF) browser instance in an async fashion. " + + "The undelying CefBrowser instance is not yet initialized. Use the IsBrowserInitializedChanged event and check " + + "the IsBrowserInitialized property to determine when the browser has been initialized."; + private const string CefInitializeFailedErrorMessage = "Cef.Initialize() failed.Check the log file see https://github.com/cefsharp/CefSharp/wiki/Trouble-Shooting#log-file for details."; + private const string CefIsInitializedFalseErrorMessage = "Cef.IsInitialized was false!.Check the log file for errors!. See https://github.com/cefsharp/CefSharp/wiki/Trouble-Shooting#log-file for details."; + + [DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)] + private static extern IntPtr CreateWindowEx(int dwExStyle, + string lpszClassName, + string lpszWindowName, + int style, + int x, int y, + int width, int height, + IntPtr hwndParent, + IntPtr hMenu, + IntPtr hInst, + [MarshalAs(UnmanagedType.AsAny)] object pvParam); + + [DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)] + private static extern bool DestroyWindow(IntPtr hwnd); + + [DllImport("user32.dll", EntryPoint = "GetWindowLong")] + private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int index); + + [DllImport("user32.dll", EntryPoint = "SetWindowLong")] + private static extern int SetWindowLong32(HandleRef hWnd, int index, int dwNewLong); + + [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")] + private static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int index, IntPtr dwNewLong); + + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongptra + //SetWindowLongPtr for x64, SetWindowLong for x86 + private void RemoveExNoActivateStyle(IntPtr hwnd) + { + var exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE); + + if (IntPtr.Size == 8) + { + if ((exStyle.ToInt64() & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE) + { + exStyle = new IntPtr(exStyle.ToInt64() & ~WS_EX_NOACTIVATE); + //Remove WS_EX_NOACTIVATE + SetWindowLongPtr64(new HandleRef(this, hwnd), GWL_EXSTYLE, exStyle); + } + } + else + { + if ((exStyle.ToInt32() & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE) + { + //Remove WS_EX_NOACTIVATE + SetWindowLong32(new HandleRef(this, hwnd), GWL_EXSTYLE, (int)(exStyle.ToInt32() & ~WS_EX_NOACTIVATE)); + } + } + } + + private const int WS_CHILD = 0x40000000, + WS_VISIBLE = 0x10000000, + LBS_NOTIFY = 0x00000001, + HOST_ID = 0x00000002, + LISTBOX_ID = 0x00000001, + WS_VSCROLL = 0x00200000, + WS_BORDER = 0x00800000, + WS_CLIPCHILDREN = 0x02000000; + + private const uint WS_EX_NOACTIVATE = 0x08000000; + private const int GWL_EXSTYLE = -20; + + /// + /// Handle we'll use to host the browser + /// + private IntPtr hwndHost; + /// + /// The managed cef browser adapter + /// + private IBrowserAdapter managedCefBrowserAdapter; + /// + /// The ignore URI change + /// + private bool ignoreUriChange; + /// + /// Initial address + /// + private string initialAddress; + /// + /// Used to stop multiple threads trying to load the initial Url multiple times. + /// If the Address property is bound after the browser is initialized + /// + private bool initialLoadCalled; + /// + /// Has the underlying Cef Browser been created (slightly different to initliazed in that + /// the browser is initialized in an async fashion) + /// + private bool browserCreated; + /// + /// The browser initialized - boolean represented as 0 (false) and 1(true) as we use Interlocker to increment/reset + /// + private int browserInitialized; + /// + /// The browser + /// + private IBrowser browser; + /// + /// Browser initialization settings + /// + private IBrowserSettings browserSettings; + /// + /// The request context (we deliberately use a private variable so we can throw an exception if + /// user attempts to set after browser created) + /// + private IRequestContext requestContext; + /// + /// A flag that indicates whether or not the designer is active + /// NOTE: Needs to be static for OnApplicationExit + /// + private static bool DesignMode; + + /// + /// The value for disposal, if it's 1 (one) then this instance is either disposed + /// or in the process of getting disposed + /// + private int disposeSignaled; + + /// + /// If true the the WS_EX_NOACTIVATE style will be removed so that future mouse clicks + /// inside the browser correctly activate and focus the window. + /// + private bool removeExNoActivateStyle; + + /// + /// Current DPI Scale + /// + private double dpiScale; + + /// + /// The HwndSource RootVisual (Window) - We store a reference + /// to unsubscribe event handlers + /// + private Window sourceWindow; + + /// + /// Store the previous window state, used to determine if the + /// Windows was previous + /// and resume rendering + /// + private WindowState previousWindowState; + + /// + /// This flag is set when the browser gets focus before the underlying CEF browser + /// has been initialized. + /// + private bool initialFocus; + + /// + /// Initial browser load task complection source + /// + private TaskCompletionSource initialLoadTaskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + /// + /// Initial browser load action + /// + private Action initialLoadAction; + + /// + /// Activates browser upon creation, the default value is false. Prior to version 73 + /// the default behaviour was to activate browser on creation (Equivilent of setting this property to true). + /// To restore this behaviour set this value to true immediately after you create the instance. + /// https://bitbucket.org/chromiumembedded/cef/issues/1856/branch-2526-cef-activates-browser-window + /// + public bool ActivateBrowserOnCreation { get; set; } + + /// + /// Gets a value indicating whether this instance is disposed. + /// + /// if this instance is disposed; otherwise, . + public bool IsDisposed + { + get + { + return Interlocked.CompareExchange(ref disposeSignaled, 1, 1) == 1; + } + } + + /// + /// Gets or sets the browser settings. + /// + /// The browser settings. + public IBrowserSettings BrowserSettings + { + get { return browserSettings; } + set + { + if (browserCreated) + { + throw new Exception("Browser has already been created. BrowserSettings must be " + + "set before the underlying CEF browser is created."); + } + + //New instance is created in the constructor, if you use + //xaml to initialize browser settings then it will also create a new + //instance, so we dispose of the old one + if (browserSettings != null && browserSettings.AutoDispose) + { + browserSettings.Dispose(); + } + + browserSettings = value; + } + } + /// + /// Gets or sets the request context. + /// + /// The request context. + public IRequestContext RequestContext + { + get { return requestContext; } + set + { + if (browserCreated) + { + throw new Exception("Browser has already been created. RequestContext must be " + + "set before the underlying CEF browser is created."); + } + if (value != null && !Core.ObjectFactory.RequestContextType.IsAssignableFrom(value.UnWrap().GetType())) + { + throw new Exception(string.Format("RequestContext can only be of type {0} or null", Core.ObjectFactory.RequestContextType)); + } + requestContext = value; + } + } + /// + /// Implement and assign to handle dialog events. + /// + /// The dialog handler. + public IDialogHandler DialogHandler { get; set; } + /// + /// Implement and assign to handle events related to JavaScript Dialogs. + /// + /// The js dialog handler. + public IJsDialogHandler JsDialogHandler { get; set; } + /// + /// Implement and assign to handle events related to key press. + /// + /// The keyboard handler. + public IKeyboardHandler KeyboardHandler { get; set; } + /// + /// Implement and assign to handle events related to browser requests. + /// + /// The request handler. + public IRequestHandler RequestHandler { get; set; } + /// + /// Implement and assign to handle events related to downloading files. + /// + /// The download handler. + public IDownloadHandler DownloadHandler { get; set; } + /// + /// Implement and assign to handle events related to browser load status. + /// + /// The load handler. + public ILoadHandler LoadHandler { get; set; } + /// + /// Implement and assign to handle events related to popups. + /// + /// The life span handler. + public ILifeSpanHandler LifeSpanHandler { get; set; } + /// + /// Implement and assign to handle events related to browser display state. + /// + /// The display handler. + public IDisplayHandler DisplayHandler { get; set; } + /// + /// Implement and assign to handle events related to the browser context menu + /// + /// The menu handler. + public IContextMenuHandler MenuHandler { get; set; } + /// + /// Implement and assign to handle events related to the browser component's focus + /// + /// The focus handler. + public IFocusHandler FocusHandler { get; set; } + /// + /// Implement and assign to handle events related to dragging. + /// + /// The drag handler. + public IDragHandler DragHandler { get; set; } + /// + /// Implement and control the loading of resources + /// + /// The resource handler factory. + public IResourceRequestHandlerFactory ResourceRequestHandlerFactory { get; set; } + /// + /// Implement and assign to handle messages from the render process. + /// + /// The render process message handler. + public IRenderProcessMessageHandler RenderProcessMessageHandler { get; set; } + /// + /// Implement to handle events related to find results. + /// + /// The find handler. + public IFindHandler FindHandler { get; set; } + + /// + /// Implement to handle audio events. + /// + public IAudioHandler AudioHandler { get; set; } + + /// + /// Implement to handle frame events. + /// + public IFrameHandler FrameHandler { get; set; } + + /// + /// Implement to handle events related to permission requests. + /// + public IPermissionHandler PermissionHandler { get; set; } + + /// + /// Event handler for receiving Javascript console messages being sent from web pages. + /// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI + /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang.. + /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread. + /// (The exception to this is when your running with settings.MultiThreadedMessageLoop = false, then they'll be the same thread). + /// + public event EventHandler ConsoleMessage; + + /// + /// Event handler for changes to the status message. + /// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI + /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang. + /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread. + /// (The exception to this is when your running with settings.MultiThreadedMessageLoop = false, then they'll be the same thread). + /// + public event EventHandler StatusMessage; + + /// + /// Event handler that will get called when the browser begins loading a frame. Multiple frames may be loading at the same + /// time. Sub-frames may start or continue loading after the main frame load has ended. This method may not be called for a + /// particular frame if the load request for that frame fails. For notification of overall browser load status use + /// OnLoadingStateChange instead. + /// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI + /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang.. + /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread. + /// + /// Whilst this may seem like a logical place to execute js, it's called before the DOM has been loaded, implement + /// as it's called when the underlying V8Context is created + /// + public event EventHandler FrameLoadStart; + + /// + /// Event handler that will get called when the browser is done loading a frame. Multiple frames may be loading at the same + /// time. Sub-frames may start or continue loading after the main frame load has ended. This method will always be called + /// for all frames irrespective of whether the request completes successfully. + /// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI + /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang.. + /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread. + /// + public event EventHandler FrameLoadEnd; + + /// + /// Event handler that will get called when the resource load for a navigation fails or is canceled. + /// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI + /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang.. + /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread. + /// + public event EventHandler LoadError; + + /// + /// Event handler that will get called when the Loading state has changed. + /// This event will be fired twice. Once when loading is initiated either programmatically or + /// by user action, and once when loading is terminated due to completion, cancellation of failure. + /// It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI + /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang.. + /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread. + /// + public event EventHandler LoadingStateChanged; + + /// + /// Event handler that will get called when the message that originates from CefSharp.PostMessage + /// + public event EventHandler JavascriptMessageReceived; + + /// + /// Navigates to the previous page in the browser history. Will automatically be enabled/disabled depending on the + /// browser state. + /// + /// The back command. + public ICommand BackCommand { get; private set; } + /// + /// Navigates to the next page in the browser history. Will automatically be enabled/disabled depending on the + /// browser state. + /// + /// The forward command. + public ICommand ForwardCommand { get; private set; } + /// + /// Reloads the content of the current page. Will automatically be enabled/disabled depending on the browser state. + /// + /// The reload command. + public ICommand ReloadCommand { get; private set; } + /// + /// Prints the current browser contents. + /// + /// The print command. + public ICommand PrintCommand { get; private set; } + /// + /// Increases the zoom level. + /// + /// The zoom in command. + public ICommand ZoomInCommand { get; private set; } + /// + /// Decreases the zoom level. + /// + /// The zoom out command. + public ICommand ZoomOutCommand { get; private set; } + /// + /// Resets the zoom level to the default. (100%) + /// + /// The zoom reset command. + public ICommand ZoomResetCommand { get; private set; } + /// + /// Opens up a new program window (using the default text editor) where the source code of the currently displayed web + /// page is shown. + /// + /// The view source command. + public ICommand ViewSourceCommand { get; private set; } + /// + /// Command which cleans up the Resources used by the ChromiumWebBrowser + /// + /// The cleanup command. + public ICommand CleanupCommand { get; private set; } + /// + /// Stops loading the current page. + /// + /// The stop command. + public ICommand StopCommand { get; private set; } + /// + /// Cut selected text to the clipboard. + /// + /// The cut command. + public ICommand CutCommand { get; private set; } + /// + /// Copy selected text to the clipboard. + /// + /// The copy command. + public ICommand CopyCommand { get; private set; } + /// + /// Paste text from the clipboard. + /// + /// The paste command. + public ICommand PasteCommand { get; private set; } + /// + /// Select all text. + /// + /// The select all command. + public ICommand SelectAllCommand { get; private set; } + /// + /// Undo last action. + /// + /// The undo command. + public ICommand UndoCommand { get; private set; } + /// + /// Redo last action. + /// + /// The redo command. + public ICommand RedoCommand { get; private set; } + + /// + public ICommand ToggleAudioMuteCommand { get; private set; } + + /// + /// Used as workaround for issue https://github.com/cefsharp/CefSharp/issues/3021 + /// + private int canExecuteJavascriptInMainFrameChildProcessId; + + /// + /// A flag that indicates if you can execute javascript in the main frame. + /// Flag is set to true in IRenderProcessMessageHandler.OnContextCreated. + /// and false in IRenderProcessMessageHandler.OnContextReleased + /// + public bool CanExecuteJavascriptInMainFrame { get; private set; } + + /// + /// Initializes static members of the class. + /// + //TODO: Currently only a single Dispatcher is supported, all controls will + //be Shutdown on that dispatcher which may not actually be the correct Dispatcher. + //We could potentially use a ThreadStatic variable to implement this behaviour + //and support multiple dispatchers. + static ChromiumWebBrowser() + { + if (CefSharpSettings.ShutdownOnExit) + { + //Use Dispatcher.FromThread as it returns null if no dispatcher + //is available for this thread. + var dispatcher = Dispatcher.FromThread(Thread.CurrentThread); + if (dispatcher == null) + { + //No dispatcher then we'll rely on Application.Exit + var app = Application.Current; + + if (app != null) + { + app.Exit += OnApplicationExit; + } + } + else + { + dispatcher.ShutdownStarted += DispatcherShutdownStarted; + dispatcher.ShutdownFinished += DispatcherShutdownFinished; + } + } + } + + /// + /// Handles Dispatcher Shutdown + /// + /// sender + /// eventargs + private static void DispatcherShutdownStarted(object sender, EventArgs e) + { + var dispatcher = (Dispatcher)sender; + + dispatcher.ShutdownStarted -= DispatcherShutdownStarted; + + if (!DesignMode) + { + CefPreShutdown(); + } + } + + private static void DispatcherShutdownFinished(object sender, EventArgs e) + { + var dispatcher = (Dispatcher)sender; + + dispatcher.ShutdownFinished -= DispatcherShutdownFinished; + + if (!DesignMode) + { + CefShutdown(); + } + } + + /// + /// Handles the event. + /// + /// The sender. + /// The instance containing the event data. + private static void OnApplicationExit(object sender, ExitEventArgs e) + { + if (!DesignMode) + { + CefShutdown(); + } + } + + /// + /// Required for designer support - this method cannot be inlined as the designer + /// will attempt to load libcef.dll and will subsequently throw an exception. + /// + [MethodImpl(MethodImplOptions.NoInlining)] + private static void CefPreShutdown() + { + Cef.PreShutdown(); + } + + /// + /// Required for designer support - this method cannot be inlined as the designer + /// will attempt to load libcef.dll and will subsiquently throw an exception. + /// + [MethodImpl(MethodImplOptions.NoInlining)] + private static void CefShutdown() + { + Cef.Shutdown(); + } + + /// + /// Initializes a new instance of the instance. + /// + /// Cef::Initialize() failed + public ChromiumWebBrowser() + { + DesignMode = System.ComponentModel.DesignerProperties.GetIsInDesignMode(this); + + if (!DesignMode) + { + NoInliningConstructor(); + } + } + + /// + /// Initializes a new instance of the instance. + /// + /// address to load initially + public ChromiumWebBrowser(string initialAddress) + { + this.initialAddress = initialAddress; + + NoInliningConstructor(); + } + + /// + /// Constructor logic has been moved into this method + /// Required for designer support - this method cannot be inlined as the designer + /// will attempt to load libcef.dll and will subsiquently throw an exception. + /// + [MethodImpl(MethodImplOptions.NoInlining)] + private void NoInliningConstructor() + { + InitializeCefInternal(); + + //Add this ChromiumWebBrowser instance to a list of IDisposable objects + // that if still alive at the time Cef.Shutdown is called will be disposed of + // It's important all browser instances be freed before Cef.Shutdown is called. + Cef.AddDisposable(this); + Focusable = true; + FocusVisualStyle = null; + + WebBrowser = this; + + SizeChanged += OnSizeChanged; + IsVisibleChanged += OnIsVisibleChanged; + + BackCommand = new DelegateCommand(this.Back, () => CanGoBack); + ForwardCommand = new DelegateCommand(this.Forward, () => CanGoForward); + ReloadCommand = new DelegateCommand(this.Reload, () => !IsLoading); + PrintCommand = new DelegateCommand(this.Print); + ZoomInCommand = new DelegateCommand(ZoomIn); + ZoomOutCommand = new DelegateCommand(ZoomOut); + ZoomResetCommand = new DelegateCommand(ZoomReset); + ViewSourceCommand = new DelegateCommand(this.ViewSource); + CleanupCommand = new DelegateCommand(Dispose); + StopCommand = new DelegateCommand(this.Stop); + CutCommand = new DelegateCommand(this.Cut); + CopyCommand = new DelegateCommand(this.Copy); + PasteCommand = new DelegateCommand(this.Paste); + SelectAllCommand = new DelegateCommand(this.SelectAll); + UndoCommand = new DelegateCommand(this.Undo); + RedoCommand = new DelegateCommand(this.Redo); + ToggleAudioMuteCommand = new DelegateCommand(this.ToggleAudioMute); + + managedCefBrowserAdapter = ManagedCefBrowserAdapter.Create(this, false); + + browserSettings = new BrowserSettings(autoDispose:true); + + PresentationSource.AddSourceChangedHandler(this, PresentationSourceChangedHandler); + + FocusHandler = new FocusHandler(); + LifeSpanHandler = new NoCloseLifespanHandler(); + + UseLayoutRounding = true; + } + + private void PresentationSourceChangedHandler(object sender, SourceChangedEventArgs args) + { + if (args.NewSource != null) + { + var source = (HwndSource)args.NewSource; + + var matrix = source.CompositionTarget.TransformToDevice; + + dpiScale = matrix.M11; + + var window = source.RootVisual as Window; + if (window != null) + { + window.StateChanged += OnWindowStateChanged; + window.LocationChanged += OnWindowLocationChanged; + sourceWindow = window; + + if (CleanupElement == null) + { + CleanupElement = window; + } + else if (CleanupElement is Window parent) + { + //If the CleanupElement is a window then move it to the new Window + if (parent != window) + { + CleanupElement = window; + } + } + } + } + else if (args.OldSource != null) + { + var window = args.OldSource.RootVisual as Window; + if (window != null) + { + window.StateChanged -= OnWindowStateChanged; + window.LocationChanged -= OnWindowLocationChanged; + sourceWindow = null; + } + } + } + + /// + protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi) + { + dpiScale = newDpi.DpiScaleX; + + //If the DPI changed then we need to resize. + ResizeBrowser((int)ActualWidth, (int)ActualHeight); + + base.OnDpiChanged(oldDpi, newDpi); + } + + private void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + ResizeBrowser((int)e.NewSize.Width, (int)e.NewSize.Height); + } + + /// + protected override HandleRef BuildWindowCore(HandleRef hwndParent) + { + if (hwndHost == IntPtr.Zero) + { + hwndHost = CreateWindowEx(0, "static", "", + WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, + 0, 0, + (int)ActualWidth, (int)ActualHeight, + hwndParent.Handle, + (IntPtr)HOST_ID, + IntPtr.Zero, + 0); + } + + CreateBrowser(); + + if (browserSettings.AutoDispose) + { + browserSettings.Dispose(); + } + + browserSettings = null; + + // When CEF is integrated into main message loop + // We unregister the WPF keyboard KeyboardInputSite to make sure + // TAB works correctly + if (Cef.CurrentlyOnThread(CefThreadIds.TID_UI)) + { + var keyboardInputSite = ((IKeyboardInputSink)this).KeyboardInputSite; + if (keyboardInputSite != null) + { + ((IKeyboardInputSink)this).KeyboardInputSite = null; + + keyboardInputSite.Unregister(); + } + } + + return new HandleRef(null, hwndHost); + } + + /// + protected override void DestroyWindowCore(HandleRef hwnd) + { + DestroyWindow(hwnd.Handle); + } + + /// + protected override bool TabIntoCore(TraversalRequest request) + { + if(InternalIsBrowserInitialized()) + { + var host = browser.GetHost(); + host.SetFocus(true); + + return true; + } + + return base.TabIntoCore(request); + } + + /// + protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) + { + if(!e.Handled) + { + if (InternalIsBrowserInitialized()) + { + var host = browser.GetHost(); + host.SetFocus(true); + } + else + { + initialFocus = true; + } + } + + base.OnGotKeyboardFocus(e); + } + + /// + protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) + { + if (!e.Handled) + { + if (InternalIsBrowserInitialized()) + { + var host = browser.GetHost(); + host.SetFocus(false); + } + else + { + initialFocus = false; + } + } + + base.OnLostKeyboardFocus(e); + } + + + /// + protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + const int WM_SETFOCUS = 0x0007; + const int WM_MOUSEACTIVATE = 0x0021; + switch (msg) + { + case WM_SETFOCUS: + case WM_MOUSEACTIVATE: + { + if(InternalIsBrowserInitialized()) + { + var host = browser.GetHost(); + host.SetFocus(true); + + handled = true; + + return IntPtr.Zero; + } + break; + } + } + return base.WndProc(hwnd, msg, wParam, lParam, ref handled); + } + + /// + /// If not in design mode; Releases unmanaged and - optionally - managed resources for the + /// + /// to release both managed and unmanaged resources; to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + // Attempt to move the disposeSignaled state from 0 to 1. If successful, we can be assured that + // this thread is the first thread to do so, and can safely dispose of the object. + if (Interlocked.CompareExchange(ref disposeSignaled, 1, 0) != 0) + { + return; + } + + if (!DesignMode) + { + InternalDispose(disposing); + } + + base.Dispose(disposing); + } + + /// + /// Releases unmanaged and - optionally - managed resources for the + /// + /// to release both managed and unmanaged resources; to release only unmanaged resources. + /// + /// This method cannot be inlined as the designer will attempt to load libcef.dll and will subsiquently throw an exception. + /// + [MethodImpl(MethodImplOptions.NoInlining)] + private void InternalDispose(bool disposing) + { + Interlocked.Exchange(ref browserInitialized, 0); + + if (disposing) + { + SizeChanged -= OnSizeChanged; + IsVisibleChanged -= OnIsVisibleChanged; + + PresentationSource.RemoveSourceChangedHandler(this, PresentationSourceChangedHandler); + // Release window event listeners if PresentationSourceChangedHandler event wasn't + // fired before Dispose + if (sourceWindow != null) + { + sourceWindow.StateChanged -= OnWindowStateChanged; + sourceWindow.LocationChanged -= OnWindowLocationChanged; + sourceWindow = null; + } + + + UiThreadRunAsync(() => + { + OnIsBrowserInitializedChanged(true, false); + + //To Minic the WPF behaviour this happens after OnIsBrowserInitializedChanged + IsBrowserInitializedChanged?.Invoke(this, EventArgs.Empty); + + WebBrowser = null; + }); + + // Don't maintain a reference to event listeners anylonger: + ConsoleMessage = null; + FrameLoadEnd = null; + FrameLoadStart = null; + IsBrowserInitializedChanged = null; + LoadError = null; + LoadingStateChanged = null; + StatusMessage = null; + TitleChanged = null; + JavascriptMessageReceived = null; + + // Release reference to handlers, except LifeSpanHandler which we don't set to null + //so that ILifeSpanHandler.DoClose will not be invoked. Previously we set LifeSpanHandler = null + //after managedCefBrowserAdapter.Dispose, there's still cases where the LifeSpanHandler was null + //when DoClose was called Issue https://github.com/cefsharp/CefSharp.Wpf.HwndHost/issues/10 + FindHandler = null; + DialogHandler = null; + RequestHandler = null; + DisplayHandler = null; + LoadHandler = null; + KeyboardHandler = null; + JsDialogHandler = null; + DragHandler = null; + DownloadHandler = null; + MenuHandler = null; + FocusHandler = null; + ResourceRequestHandlerFactory = null; + RenderProcessMessageHandler = null; + + this.DisposeDevToolsContext(); + + browser = null; + BrowserCore = null; + + if (CleanupElement != null) + { + CleanupElement.Unloaded -= OnCleanupElementUnloaded; + } + + managedCefBrowserAdapter?.Dispose(); + managedCefBrowserAdapter = null; + } + + Cef.RemoveDisposable(this); + } + + /// + /// Sets the address. + /// + /// The instance containing the event data. + void IWebBrowserInternal.SetAddress(AddressChangedEventArgs args) + { + UiThreadRunAsync(() => + { + ignoreUriChange = true; + SetCurrentValue(AddressProperty, args.Address); + ignoreUriChange = false; + + // The tooltip should obviously also be reset (and hidden) when the address changes. + SetCurrentValue(TooltipTextProperty, null); + }); + } + + /// + /// Sets the loading state change. + /// + /// The instance containing the event data. + void IWebBrowserInternal.SetLoadingStateChange(LoadingStateChangedEventArgs args) + { + if (removeExNoActivateStyle && InternalIsBrowserInitialized()) + { + removeExNoActivateStyle = false; + + var host = this.GetBrowserHost(); + var hwnd = host.GetWindowHandle(); + //Remove the WS_EX_NOACTIVATE style so that future mouse clicks inside the + //browser correctly activate and focus the browser. + //https://github.com/chromiumembedded/cef/blob/9df4a54308a88fd80c5774d91c62da35afb5fd1b/tests/cefclient/browser/root_window_win.cc#L1088 + RemoveExNoActivateStyle(hwnd); + } + + UiThreadRunAsync(() => + { + SetCurrentValue(CanGoBackProperty, args.CanGoBack); + SetCurrentValue(CanGoForwardProperty, args.CanGoForward); + SetCurrentValue(IsLoadingProperty, args.IsLoading); + + ((DelegateCommand)BackCommand).RaiseCanExecuteChanged(); + ((DelegateCommand)ForwardCommand).RaiseCanExecuteChanged(); + ((DelegateCommand)ReloadCommand).RaiseCanExecuteChanged(); + }); + + LoadingStateChanged?.Invoke(this, args); + + initialLoadAction?.Invoke(args.IsLoading, null); + } + + /// + /// Sets the title. + /// + /// The instance containing the event data. + void IWebBrowserInternal.SetTitle(TitleChangedEventArgs args) + { + UiThreadRunAsync(() => SetCurrentValue(TitleProperty, args.Title)); + } + + /// + /// Sets the tooltip text. + /// + /// The tooltip text. + void IWebBrowserInternal.SetTooltipText(string tooltipText) + { + UiThreadRunAsync(() => SetCurrentValue(TooltipTextProperty, tooltipText)); + } + + /// + /// Handles the event. + /// + /// The instance containing the event data. + void IWebBrowserInternal.OnFrameLoadStart(FrameLoadStartEventArgs args) + { + FrameLoadStart?.Invoke(this, args); + } + + /// + /// Handles the event. + /// + /// The instance containing the event data. + void IWebBrowserInternal.OnFrameLoadEnd(FrameLoadEndEventArgs args) + { + FrameLoadEnd?.Invoke(this, args); + } + + /// + /// Handles the event. + /// + /// The instance containing the event data. + void IWebBrowserInternal.OnConsoleMessage(ConsoleMessageEventArgs args) + { + ConsoleMessage?.Invoke(this, args); + } + + /// + /// Handles the event. + /// + /// The instance containing the event data. + void IWebBrowserInternal.OnStatusMessage(StatusMessageEventArgs args) + { + StatusMessage?.Invoke(this, args); + } + + /// + /// Handles the event. + /// + /// The instance containing the event data. + void IWebBrowserInternal.OnLoadError(LoadErrorEventArgs args) + { + LoadError?.Invoke(this, args); + + initialLoadAction?.Invoke(null, args.ErrorCode); + } + + void IWebBrowserInternal.SetCanExecuteJavascriptOnMainFrame(string frameId, bool canExecute) + { + //When loading pages of a different origin the frameId changes + //For the first loading of a new origin the messages from the render process + //Arrive in a different order than expected, the OnContextCreated message + //arrives before the OnContextReleased, then the message for OnContextReleased + //incorrectly overrides the value + //https://github.com/cefsharp/CefSharp/issues/3021 + + var chromiumChildProcessId = GetChromiumChildProcessId(frameId); + + if (chromiumChildProcessId > canExecuteJavascriptInMainFrameChildProcessId && !canExecute) + { + return; + } + + canExecuteJavascriptInMainFrameChildProcessId = chromiumChildProcessId; + CanExecuteJavascriptInMainFrame = canExecute; + } + + void IWebBrowserInternal.SetJavascriptMessageReceived(JavascriptMessageReceivedEventArgs args) + { + JavascriptMessageReceived?.Invoke(this, args); + } + + /// + /// Gets the browser adapter. + /// + /// The browser adapter. + IBrowserAdapter IWebBrowserInternal.BrowserAdapter + { + get { return managedCefBrowserAdapter; } + } + + /// + /// Gets or sets a value indicating whether this instance has parent. + /// + /// true if this instance has parent; otherwise, false. + bool IWebBrowserInternal.HasParent { get; set; } + + /// + /// Called when [after browser created]. + /// + /// The browser. + void IWebBrowserInternal.OnAfterBrowserCreated(IBrowser browser) + { + if (IsDisposed || browser.IsDisposed) + { + return; + } + + this.browser = browser; + BrowserCore = browser; + initialLoadAction = InitialLoad; + Interlocked.Exchange(ref browserInitialized, 1); + + UiThreadRunAsync(() => + { + if (!IsDisposed) + { + OnIsBrowserInitializedChanged(false, true); + //To Minic the WPF behaviour this happens after OnIsBrowserInitializedChanged + IsBrowserInitializedChanged?.Invoke(this, EventArgs.Empty); + + // Only call Load if initialAddress is null and Address is not empty + if (string.IsNullOrEmpty(initialAddress) && !string.IsNullOrEmpty(Address) && !initialLoadCalled) + { + Load(Address); + } + } + }); + + ResizeBrowser((int)ActualWidth, (int)ActualHeight); + + if (initialFocus) + { + browser.GetHost()?.SetFocus(true); + } + } + + /// + /// A flag that indicates whether the state of the control current supports the GoBack action (true) or not (false). + /// + /// true if this instance can go back; otherwise, false. + /// In the WPF control, this property is implemented as a Dependency Property and fully supports data + /// binding. + public bool CanGoBack + { + get { return (bool)GetValue(CanGoBackProperty); } + } + + /// + /// The can go back property + /// + public static DependencyProperty CanGoBackProperty = DependencyProperty.Register(nameof(CanGoBack), typeof(bool), typeof(ChromiumWebBrowser)); + + /// + /// A flag that indicates whether the state of the control currently supports the GoForward action (true) or not (false). + /// + /// true if this instance can go forward; otherwise, false. + /// In the WPF control, this property is implemented as a Dependency Property and fully supports data + /// binding. + public bool CanGoForward + { + get { return (bool)GetValue(CanGoForwardProperty); } + } + + /// + /// The can go forward property + /// + public static DependencyProperty CanGoForwardProperty = DependencyProperty.Register(nameof(CanGoForward), typeof(bool), typeof(ChromiumWebBrowser)); + + /// + /// The address (URL) which the browser control is currently displaying. + /// Will automatically be updated as the user navigates to another page (e.g. by clicking on a link). + /// + /// The address. + /// In the WPF control, this property is implemented as a Dependency Property and fully supports data + /// binding. + public string Address + { + get { return (string)GetValue(AddressProperty); } + set { SetValue(AddressProperty, value); } + } + + /// + /// The address property + /// + public static readonly DependencyProperty AddressProperty = + DependencyProperty.Register(nameof(Address), typeof(string), typeof(ChromiumWebBrowser), + new UIPropertyMetadata(null, OnAddressChanged)); + + /// + /// Handles the event. + /// + /// The sender. + /// The instance containing the event data. + private static void OnAddressChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) + { + var owner = (ChromiumWebBrowser)sender; + var oldValue = (string)args.OldValue; + var newValue = (string)args.NewValue; + + owner.OnAddressChanged(oldValue, newValue); + } + + /// + /// Called when [address changed]. + /// + /// The old value. + /// The new value. + protected virtual void OnAddressChanged(string oldValue, string newValue) + { + if (ignoreUriChange || newValue == null) + { + return; + } + + Load(newValue); + } + + /// + /// A flag that indicates whether the control is currently loading one or more web pages (true) or not (false). + /// + /// true if this instance is loading; otherwise, false. + /// In the WPF control, this property is implemented as a Dependency Property and fully supports data + /// binding. + public bool IsLoading + { + get { return (bool)GetValue(IsLoadingProperty); } + } + + /// + /// The is loading property + /// + public static readonly DependencyProperty IsLoadingProperty = + DependencyProperty.Register(nameof(IsLoading), typeof(bool), typeof(ChromiumWebBrowser), new PropertyMetadata(false)); + + /// + /// A flag that indicates whether the WebBrowser is initialized (true) or not (false). + /// + /// true if this instance is browser initialized; otherwise, false. + /// In the WPF control, this property is implemented as a Dependency Property and fully supports data + /// binding. + public bool IsBrowserInitialized + { + get { return InternalIsBrowserInitialized(); } + } + + /// + /// Event called after the underlying CEF browser instance has been created and + /// when the instance has been Disposed. + /// will be true when the underlying CEF Browser + /// has been created and false when the browser is being Disposed. + /// + public event EventHandler IsBrowserInitializedChanged; + + /// + /// Called when [is browser initialized changed]. + /// + /// if set to true [old value]. + /// if set to true [new value]. + protected virtual void OnIsBrowserInitializedChanged(bool oldValue, bool newValue) + { + if (newValue && !IsDisposed) + { + var task = this.GetZoomLevelAsync(); + task.ContinueWith(previous => + { + if (previous.Status == TaskStatus.RanToCompletion) + { + UiThreadRunAsync(() => + { + if (!IsDisposed) + { + SetCurrentValue(ZoomLevelProperty, previous.Result); + } + }); + } + else + { + throw new InvalidOperationException("Unexpected failure of calling CEF->GetZoomLevelAsync", previous.Exception); + } + }, TaskContinuationOptions.ExecuteSynchronously); + } + } + + /// + /// The title of the web page being currently displayed. + /// + /// The title. + /// This property is implemented as a Dependency Property and fully supports data binding. + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + /// + /// The title property + /// + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register(nameof(Title), typeof(string), typeof(ChromiumWebBrowser), new PropertyMetadata(null, OnTitleChanged)); + + /// + /// Event handler that will get called when the browser title changes + /// + public event DependencyPropertyChangedEventHandler TitleChanged; + + /// + /// Handles the event. + /// + /// The d. + /// The instance containing the event data. + private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var owner = (ChromiumWebBrowser)d; + + owner.TitleChanged?.Invoke(owner, e); + } + + /// + /// The zoom level at which the browser control is currently displaying. + /// Can be set to 0 to clear the zoom level (resets to default zoom level). + /// + /// The zoom level. + public double ZoomLevel + { + get { return (double)GetValue(ZoomLevelProperty); } + set { SetValue(ZoomLevelProperty, value); } + } + + /// + /// The zoom level property + /// + public static readonly DependencyProperty ZoomLevelProperty = + DependencyProperty.Register(nameof(ZoomLevel), typeof(double), typeof(ChromiumWebBrowser), + new UIPropertyMetadata(0d, OnZoomLevelChanged)); + + /// + /// Handles the event. + /// + /// The sender. + /// The instance containing the event data. + private static void OnZoomLevelChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) + { + var owner = (ChromiumWebBrowser)sender; + var oldValue = (double)args.OldValue; + var newValue = (double)args.NewValue; + + owner.OnZoomLevelChanged(oldValue, newValue); + } + + /// + /// Called when [zoom level changed]. + /// + /// The old value. + /// The new value. + protected virtual void OnZoomLevelChanged(double oldValue, double newValue) + { + this.SetZoomLevel(newValue); + } + + /// + /// Specifies the amount used to increase/decrease to ZoomLevel by + /// By Default this value is 0.10 + /// + /// The zoom level increment. + public double ZoomLevelIncrement + { + get { return (double)GetValue(ZoomLevelIncrementProperty); } + set { SetValue(ZoomLevelIncrementProperty, value); } + } + + /// + /// The zoom level increment property + /// + public static readonly DependencyProperty ZoomLevelIncrementProperty = + DependencyProperty.Register(nameof(ZoomLevelIncrement), typeof(double), typeof(ChromiumWebBrowser), new PropertyMetadata(0.10)); + + /// + /// The CleanupElement controls when the Browser will be Disposed. + /// The will be Disposed when is called. + /// Be aware that this Control is not usable anymore after it has been disposed. + /// + /// The cleanup element. + public FrameworkElement CleanupElement + { + get { return (FrameworkElement)GetValue(CleanupElementProperty); } + set { SetValue(CleanupElementProperty, value); } + } + + /// + /// The cleanup element property + /// + public static readonly DependencyProperty CleanupElementProperty = + DependencyProperty.Register(nameof(CleanupElement), typeof(FrameworkElement), typeof(ChromiumWebBrowser), new PropertyMetadata(null, OnCleanupElementChanged)); + + /// + /// Handles the event. + /// + /// The sender. + /// The instance containing the event data. + private static void OnCleanupElementChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) + { + var owner = (ChromiumWebBrowser)sender; + var oldValue = (FrameworkElement)args.OldValue; + var newValue = (FrameworkElement)args.NewValue; + + owner.OnCleanupElementChanged(oldValue, newValue); + } + + /// + /// Called when [cleanup element changed]. + /// + /// The old value. + /// The new value. + protected virtual void OnCleanupElementChanged(FrameworkElement oldValue, FrameworkElement newValue) + { + if (oldValue != null) + { + oldValue.Unloaded -= OnCleanupElementUnloaded; + } + + if (newValue != null) + { + newValue.Unloaded += OnCleanupElementUnloaded; + } + } + + + /// + /// Handles the event. + /// + /// The sender. + /// The instance containing the event data. + private void OnCleanupElementUnloaded(object sender, RoutedEventArgs e) + { + Dispose(); + } + + /// + /// The text that will be displayed as a ToolTip + /// + /// The tooltip text. + public string TooltipText + { + get { return (string)GetValue(TooltipTextProperty); } + } + + /// + /// The tooltip text property + /// + public static readonly DependencyProperty TooltipTextProperty = + DependencyProperty.Register(nameof(TooltipText), typeof(string), typeof(ChromiumWebBrowser)); + + /// + /// Gets or sets the WebBrowser. + /// + /// The WebBrowser. + public IWebBrowser WebBrowser + { + get { return (IWebBrowser)GetValue(WebBrowserProperty); } + set { SetValue(WebBrowserProperty, value); } + } + + /// + /// The WebBrowser property + /// + public static readonly DependencyProperty WebBrowserProperty = + DependencyProperty.Register(nameof(WebBrowser), typeof(IWebBrowser), typeof(ChromiumWebBrowser), new UIPropertyMetadata(defaultValue: null)); + + /// + /// Override this method to handle creation of WindowInfo. This method can be used to customise aspects of + /// browser creation including configuration of settings such as . + /// Window Activation is disabled by default, you can re-enable it by overriding and removing the + /// WS_EX_NOACTIVATE style from . + /// + /// Window handle for the Control + /// Window Info + /// + /// To re-enable Window Activation then remove WS_EX_NOACTIVATE from ExStyle + /// + /// const uint WS_EX_NOACTIVATE = 0x08000000; + /// windowInfo.ExStyle &= ~WS_EX_NOACTIVATE; + /// + /// + protected virtual IWindowInfo CreateBrowserWindowInfo(IntPtr handle) + { + var windowInfo = Core.ObjectFactory.CreateWindowInfo(); + windowInfo.RuntimeStyle = CefSharpSettings.RuntimeStyle ?? CefRuntimeStyle.Alloy; + windowInfo.SetAsChild(handle); + + if (!ActivateBrowserOnCreation) + { + //Disable Window activation by default + //https://bitbucket.org/chromiumembedded/cef/issues/1856/branch-2526-cef-activates-browser-window + windowInfo.ExStyle |= WS_EX_NOACTIVATE; + } + + return windowInfo; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void CreateBrowser() + { + browserCreated = true; + + if (((IWebBrowserInternal)this).HasParent == false) + { + var windowInfo = CreateBrowserWindowInfo(hwndHost); + + //We actually check if WS_EX_NOACTIVATE was set for instances + //the user has override CreateBrowserWindowInfo and not called base.CreateBrowserWindowInfo + removeExNoActivateStyle = (windowInfo.ExStyle & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE; + + //If initialAddress is set then we use that value, later in OnAfterBrowserCreated then we will + //call Load(url) if initial address was empty. + managedCefBrowserAdapter.CreateBrowser(windowInfo, browserSettings as BrowserSettings, requestContext as RequestContext, initialAddress); + } + } + + /// + /// Runs the specific Action on the Dispatcher in an async fashion + /// + /// The action. + /// The priority. + internal void UiThreadRunAsync(Action action, DispatcherPriority priority = DispatcherPriority.DataBind) + { + if (Dispatcher.CheckAccess()) + { + action(); + } + else if (!Dispatcher.HasShutdownStarted) + { + Dispatcher.BeginInvoke(action, priority); + } + } + + /// + /// Handles the event. + /// + /// The sender. + /// The instance containing the event data. + private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args) + { + var isVisible = (bool)args.NewValue; + + if (InternalIsBrowserInitialized()) + { + var host = browser.GetHost(); + + if (isVisible) + { + ResizeBrowser((int)ActualWidth, (int)ActualHeight); + + //Fix for #1778 - When browser becomes visible we update the zoom level + //browsers of the same origin will share the same zoomlevel and + //we need to track the update, so our ZoomLevelProperty works + //properly + host.GetZoomLevelAsync().ContinueWith(t => + { + if (!IsDisposed) + { + SetCurrentValue(ZoomLevelProperty, t.Result); + } + }, + CancellationToken.None, + TaskContinuationOptions.OnlyOnRanToCompletion, + TaskScheduler.FromCurrentSynchronizationContext()); + } + else + { + //Hide browser + ResizeBrowser(0, 0); + } + } + } + + /// + /// Loads the specified URL. + /// + /// The URL to be loaded. + public void Load(string url) + { + if (IsDisposed) + { + return; + } + + //If the browser is already initialized then we can call LoadUrl directly + if (InternalIsBrowserInitialized()) + { + var b = browser; + // Added null check -> binding-triggered changes of Address will lead to a nullref after Dispose has been called + if (b != null) + { + initialLoadCalled = true; + + using (var frame = b.MainFrame) + { + frame.LoadUrl(url); + } + } + } + //If CreateBrowser was called and InternalIsBrowserInitialized() == false then we need to set the Address + //property so in OnAfterBrowserCreated the Url is loaded. If initialAddress was + //set then the Url set here will be ignored. If we called Load(url) then historically + //an aborted error would be raised as per https://github.com/cefsharp/CefSharp/issues/2300 + //So we ignore the call for now. + else if (browserCreated) + { + UiThreadRunAsync(() => + { + Address = url; + }); + } + //Before browser created, set the intialAddress + else + { + initialAddress = url; + } + } + + /// + public Task LoadUrlAsync(string url) + { + //LoadUrlAsync is actually a static method so that CefSharp.Wpf.HwndHost can reuse the code + //It's not actually an extension method so we can have it included as part of the + //IWebBrowser interface + return CefSharp.WebBrowserExtensions.LoadUrlAsync(this, url); + } + + /// + /// Zooms the browser in. + /// + private void ZoomIn() + { + UiThreadRunAsync(() => + { + ZoomLevel = ZoomLevel + ZoomLevelIncrement; + }); + } + + /// + /// Zooms the browser out. + /// + private void ZoomOut() + { + UiThreadRunAsync(() => + { + ZoomLevel = ZoomLevel - ZoomLevelIncrement; + }); + } + + /// + /// Reset the browser's zoom level to default. + /// + private void ZoomReset() + { + UiThreadRunAsync(() => + { + ZoomLevel = 0; + }); + } + + /// + /// The javascript object repository, one repository per ChromiumWebBrowser instance. + /// + public IJavascriptObjectRepository JavascriptObjectRepository + { + get { return managedCefBrowserAdapter?.JavascriptObjectRepository; } + } + + /// + public IBrowser BrowserCore { get; private set; } + + /// + /// Used by CefSharp.Puppeteer to associate a single DevToolsContext with a ChromiumWebBrowser instance. + /// + IDisposable IWebBrowserInternal.DevToolsContext { get; set; } + + /// + /// Returns the current IBrowser Instance + /// + /// browser instance or null + public IBrowser GetBrowser() + { + return browser; + } + + private static void InitializeCefInternal() + { + if (Cef.IsInitialized == null) + { + if (!Cef.Initialize(new CefSettings())) + { + throw new InvalidOperationException(CefInitializeFailedErrorMessage); + } + } + + if (Cef.IsInitialized == false) + { + throw new InvalidOperationException(CefIsInitializedFalseErrorMessage); + } + } + + /// + /// Check is browserisinitialized + /// + /// true if browser is initialized + private bool InternalIsBrowserInitialized() + { + // Use CompareExchange to read the current value - if disposeCount is 1, we set it to 1, effectively a no-op + // Volatile.Read would likely use a memory barrier which I believe is unnecessary in this scenario + return Interlocked.CompareExchange(ref browserInitialized, 0, 0) == 1; + } + + /// + /// Resizes the browser. + /// + private void ResizeBrowser(int width, int height) + { + if (InternalIsBrowserInitialized()) + { + if (dpiScale > 1) + { + width = (int)Math.Round(width * dpiScale); + height = (int)Math.Round(height * dpiScale); + } + + managedCefBrowserAdapter.Resize(width, height); + } + } + + private void OnWindowStateChanged(object sender, EventArgs e) + { + var window = (Window)sender; + + switch (window.WindowState) + { + case WindowState.Normal: + case WindowState.Maximized: + { + if (previousWindowState == WindowState.Minimized && InternalIsBrowserInitialized()) + { + ResizeBrowser((int)ActualWidth, (int)ActualHeight); + } + break; + } + case WindowState.Minimized: + { + if (InternalIsBrowserInitialized()) + { + //Set the browser size to 0,0 to reduce CPU usage + ResizeBrowser(0, 0); + } + break; + } + } + + previousWindowState = window.WindowState; + } + + private void OnWindowLocationChanged(object sender, EventArgs e) + { + if (InternalIsBrowserInitialized()) + { + var host = browser.GetHost(); + + host.NotifyMoveOrResizeStarted(); + } + } + + /// + public void LoadUrl(string url) + { + Load(url); + } + + /// + public Task WaitForInitialLoadAsync() + { + return initialLoadTaskCompletionSource.Task; + } + + private void InitialLoad(bool? isLoading, CefErrorCode? errorCode) + { + if (IsDisposed) + { + initialLoadAction = null; + + initialLoadTaskCompletionSource.TrySetCanceled(); + + return; + } + + if (isLoading.HasValue) + { + if (isLoading.Value) + { + return; + } + + initialLoadAction = null; + + var host = browser?.GetHost(); + + var navEntry = host?.GetVisibleNavigationEntry(); + + int statusCode = navEntry?.HttpStatusCode ?? -1; + + //By default 0 is some sort of error, we map that to -1 + //so that it's clearer that something failed. + if (statusCode == 0) + { + statusCode = -1; + } + + initialLoadTaskCompletionSource.TrySetResult(new LoadUrlAsyncResponse(CefErrorCode.None, statusCode)); + } + else if (errorCode.HasValue) + { + //Actions that trigger a download will raise an aborted error. + //Generally speaking Aborted is safe to ignore + if (errorCode == CefErrorCode.Aborted) + { + return; + } + + initialLoadAction = null; + + initialLoadTaskCompletionSource.TrySetResult(new LoadUrlAsyncResponse(errorCode.Value, -1)); + } + } + + /// + public bool TryGetBrowserCoreById(int browserId, out IBrowser browser) + { + var browserAdapter = managedCefBrowserAdapter; + + if (IsDisposed || browserAdapter == null || browserAdapter.IsDisposed) + { + browser = null; + + return false; + } + + browser = browserAdapter.GetBrowser(browserId); + + return browser != null; + } + + /// + public async Task GetContentSizeAsync() + { + ThrowExceptionIfDisposed(); + ThrowExceptionIfBrowserNotInitialized(); + + using (var devToolsClient = browser.GetDevToolsClient()) + { + //Get the content size + var layoutMetricsResponse = await devToolsClient.Page.GetLayoutMetricsAsync().ConfigureAwait(continueOnCapturedContext: false); + + var rect = layoutMetricsResponse.CssContentSize; + + return new Structs.DomRect(rect.X, rect.Y, rect.Width, rect.Height); + } + } + + /// + public Task WaitForNavigationAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default) + { + //WaitForNavigationAsync is actually a static method so that CefSharp.Wpf.HwndHost can reuse the code + return CefSharp.WebBrowserExtensions.WaitForNavigationAsync(this, timeout, cancellationToken); + } + + /// + /// Capture page screenshot. + /// + /// Image compression format (defaults to png). + /// Compression quality from range [0..100] (jpeg only). + /// Capture the screenshot of a given region only. + /// Capture the screenshot from the surface, rather than the view. Defaults to true. + /// Capture the screenshot beyond the viewport. Defaults to false. + /// A task that can be awaited to obtain the screenshot as a byte[]. + public async Task CaptureScreenshotAsync(CaptureScreenshotFormat format = CaptureScreenshotFormat.Png, int? quality = null, Viewport viewPort = null, bool fromSurface = true, bool captureBeyondViewport = false) + { + ThrowExceptionIfDisposed(); + ThrowExceptionIfBrowserNotInitialized(); + + if (viewPort != null && viewPort.Scale <= 0) + { + throw new ArgumentException($"{nameof(viewPort)}.{nameof(viewPort.Scale)} must be greater than 0."); + } + + using (var devToolsClient = browser.GetDevToolsClient()) + { + var screenShot = await devToolsClient.Page.CaptureScreenshotAsync(format, quality, viewPort, fromSurface, captureBeyondViewport).ConfigureAwait(continueOnCapturedContext: false); + + return screenShot.Data; + } + } + + /// + /// Throw exception if browser not initialized. + /// + /// Thrown when an exception error condition occurs. + private void ThrowExceptionIfBrowserNotInitialized() + { + if (!InternalIsBrowserInitialized()) + { + throw new Exception(BrowserNotInitializedExceptionErrorMessage); + } + } + + /// + /// Throw exception if disposed. + /// + /// Thrown when a supplied object has been disposed. + private void ThrowExceptionIfDisposed() + { + if (IsDisposed) + { + throw new ObjectDisposedException("browser", "Browser has been disposed"); + } + } + + private int GetChromiumChildProcessId(string frameIdentifier) + { + try + { + var parts = frameIdentifier.Split('-'); + + if (int.TryParse(parts[0], out var childProcessId)) + return childProcessId; + } + catch + { + + } + + return -1; + } + } +} diff --git a/CefSharp.Wpf/HwndHost/FocusHandler.cs b/CefSharp.Wpf/HwndHost/FocusHandler.cs new file mode 100644 index 0000000000..34a41a87d2 --- /dev/null +++ b/CefSharp.Wpf/HwndHost/FocusHandler.cs @@ -0,0 +1,84 @@ +// Copyright © 2020 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System.Windows.Input; + +namespace CefSharp.Wpf.HwndHost +{ + /// + /// Focus Handler + /// The methods of this class will be called on the CEF UI thread. + /// + public class FocusHandler : IFocusHandler + { + void IFocusHandler.OnGotFocus(IWebBrowser chromiumWebBrowser, IBrowser browser) + { + OnGotFocus(chromiumWebBrowser, browser); + } + + /// + /// Called when the browser component has received focus. + /// + /// the ChromiumWebBrowser control + /// the browser object + protected virtual void OnGotFocus(IWebBrowser chromiumWebBrowser, IBrowser browser) + { + + } + + bool IFocusHandler.OnSetFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, CefFocusSource source) + { + return OnSetFocus(chromiumWebBrowser, browser, source); + } + + /// + /// Called when the browser component is requesting focus. + /// + /// the ChromiumWebBrowser control + /// the browser object, do not keep a reference to this object outside of this method + /// Indicates where the focus request is originating from. + /// Return false to allow the focus to be set or true to cancel setting the focus. + protected virtual bool OnSetFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, CefFocusSource source) + { + //We don't deal with popups as they're rendered by default entirely by CEF + if (browser.IsPopup) + { + return false; + } + // Do not let the browser take focus when a Load method has been called + return source == CefFocusSource.FocusSourceNavigation; + } + + + + void IFocusHandler.OnTakeFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, bool next) + { + OnTakeFocus(chromiumWebBrowser, browser, next); + } + + /// + /// Called when the browser component is about to lose focus. + /// For instance, if focus was on the last HTML element and the user pressed the TAB key. + /// + /// the ChromiumWebBrowser control + /// the browser object + /// Will be true if the browser is giving focus to the next component + /// and false if the browser is giving focus to the previous component. + protected virtual void OnTakeFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, bool next) + { + //We don't deal with popups as they're rendered by default entirely by CEF + if (browser.IsPopup) + { + return; + } + + var hwndChromiumWebBrowser = (ChromiumWebBrowser)chromiumWebBrowser; + + var request = new TraversalRequest(next ? FocusNavigationDirection.Next : FocusNavigationDirection.Previous); + + // NOTE: OnTakeFocus means leaving focus / not taking focus + hwndChromiumWebBrowser.UiThreadRunAsync(() => hwndChromiumWebBrowser.MoveFocus(request)); + } + } +} diff --git a/CefSharp.Wpf/HwndHost/Handler/IntegratedMessageLoopBrowserProcessHandler.cs b/CefSharp.Wpf/HwndHost/Handler/IntegratedMessageLoopBrowserProcessHandler.cs new file mode 100644 index 0000000000..0a16e09aa1 --- /dev/null +++ b/CefSharp.Wpf/HwndHost/Handler/IntegratedMessageLoopBrowserProcessHandler.cs @@ -0,0 +1,131 @@ +// Copyright © 2022 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; +using System.Timers; +using System.Windows.Threading; +using CefSharp.Handler; + +namespace CefSharp.Wpf.HwndHost.Handler +{ + /// + /// A implementation that can be used to + /// integreate CEF into the WPF message loop (Dispatcher). + /// Currently it's a very basic implementation. + /// See the following link for the CEF reference implementation. + /// https://bitbucket.org/chromiumembedded/cef/commits/1ff26aa02a656b3bc9f0712591c92849c5909e04?at=2785 + /// + public class IntegratedMessageLoopBrowserProcessHandler + : BrowserProcessHandler + { + /// + /// Sixty Times per second + /// + public const int SixtyTimesPerSecond = 1000 / 60; // 60fps + /// + /// Thirty Times per second + /// + public const int ThirtyTimesPerSecond = 1000 / 30; //30fps + + private DispatcherTimer dispatcherTimer; + private Dispatcher dispatcher; + private readonly DispatcherPriority dispatcherPriority; + private int interval; + + /// + /// Default constructor + /// + /// WPF Dispatcher + /// Priority at which is called using the Dispatcher + /// the in miliseconds (frame rate), for 30/60 times per second use + /// / respectively. + public IntegratedMessageLoopBrowserProcessHandler(Dispatcher dispatcher, DispatcherPriority dispatcherPriority = DispatcherPriority.Render, int interval = ThirtyTimesPerSecond) + { + if (dispatcher == null) + { + throw new ArgumentNullException(nameof(dispatcher)); + } + + if (interval < 10) + { + throw new ArgumentOutOfRangeException(nameof(interval), "Argument less than 10. "); + } + + dispatcherTimer = new DispatcherTimer(dispatcherPriority, dispatcher) + { + Interval = TimeSpan.FromMilliseconds(interval) + }; + + dispatcherTimer.Tick += OnDispatcherTimerTick; + dispatcherTimer.Start(); + + this.dispatcher = dispatcher; + this.dispatcher.ShutdownStarted += DispatcherShutdownStarted; + this.dispatcherPriority = dispatcherPriority; + this.interval = interval; + + Cef.ShutdownStarted += OnCefShutdownStarted; + } + + private void OnCefShutdownStarted(object sender, EventArgs e) + { + InternalDispose(); + } + + private void DispatcherShutdownStarted(object sender, EventArgs e) + { + //If the dispatcher is shutting down then we will cleanup + InternalDispose(); + } + + private void OnDispatcherTimerTick(object sender, EventArgs e) + { + // Execute Cef.DoMessageLoopWork on the UI Thread + // Typically this would happen 30/60 times per second (frame rate) + Cef.DoMessageLoopWork(); + } + + /// + protected override void OnScheduleMessagePumpWork(long delay) + { + // If the delay is greater than the Maximum then use ThirtyTimesPerSecond + // instead - we do this to achieve a minimum number of FPS + if (delay > interval) + { + delay = interval; + } + + // When delay <= 0 we'll execute Cef.DoMessageLoopWork immediately + // if it's greater than we'll just let the Timer which fires 30 times per second + // care of the call + if (delay <= 0) + { + dispatcher?.InvokeAsync(() => Cef.DoMessageLoopWork(), dispatcherPriority); + } + } + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + InternalDispose(); + } + + base.Dispose(disposing); + } + + private void InternalDispose() + { + if (dispatcher != null) + { + dispatcher.ShutdownStarted -= DispatcherShutdownStarted; + dispatcher = null; + } + + dispatcherTimer?.Stop(); + dispatcherTimer = null; + } + } +} diff --git a/CefSharp.Wpf/HwndHost/IWpfWebBrowser.cs b/CefSharp.Wpf/HwndHost/IWpfWebBrowser.cs new file mode 100644 index 0000000000..d4d323c42b --- /dev/null +++ b/CefSharp.Wpf/HwndHost/IWpfWebBrowser.cs @@ -0,0 +1,18 @@ +// Copyright © 2019 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System.Windows; + +namespace CefSharp.Wpf.HwndHost +{ + /// + /// WPF specific implementation, has reference to some of the commands + /// and properties the exposes. + /// + /// + public interface IWpfWebBrowser : IWpfChromiumWebBrowser, IInputElement + { + + } +} diff --git a/CefSharp.Wpf/HwndHost/Internals/NoCloseLifespanHandler.cs b/CefSharp.Wpf/HwndHost/Internals/NoCloseLifespanHandler.cs new file mode 100644 index 0000000000..b2a9b7b50f --- /dev/null +++ b/CefSharp.Wpf/HwndHost/Internals/NoCloseLifespanHandler.cs @@ -0,0 +1,26 @@ +// Copyright © 2022 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +namespace CefSharp.Wpf.HwndHost.Internals +{ + /// + /// LifeSpanHandler used internally + /// - Cancels sending of WM_CLOSE message for main browser + /// - Allows popups to close + /// + public class NoCloseLifespanHandler + : CefSharp.Handler.LifeSpanHandler + { + /// + protected override bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser) + { + if(browser.IsPopup) + { + return false; + } + + return true; + } + } +} diff --git a/CefSharp.Wpf/IRenderHandler.cs b/CefSharp.Wpf/IRenderHandler.cs index ef4ebdc499..6a486450a2 100644 --- a/CefSharp.Wpf/IRenderHandler.cs +++ b/CefSharp.Wpf/IRenderHandler.cs @@ -18,11 +18,20 @@ public interface IRenderHandler : IDisposable /// /// Called when an element has been rendered to the shared texture handle. /// This method is only called when is set to true + /// + /// The underlying implementation uses a pool to deliver frames. As a result, + /// the handle may differ every frame depending on how many frames are + /// in-progress. The handle's resource cannot be cached and cannot be accessed + /// outside of this callback. It should be reopened each time this callback is + /// executed and the contents should be copied to a texture owned by the + /// client application. The contents of acceleratedPaintInfo + /// will be released back to the pool after this callback returns. /// /// indicates whether the element is the view or the popup widget. /// contains the set of rectangles in pixel coordinates that need to be repainted - /// is the handle for a D3D11 Texture2D that can be accessed via ID3D11Device using the OpenSharedResource method. - void OnAcceleratedPaint(bool isPopup, Rect dirtyRect, IntPtr sharedHandle); + /// contains the shared handle; on Windows it is a + /// HANDLE to a texture that can be opened with D3D11 OpenSharedResource. + void OnAcceleratedPaint(bool isPopup, Rect dirtyRect, AcceleratedPaintInfo acceleratedPaintInfo); /// /// Called when an element should be painted. (Invoked from CefRenderHandler.OnPaint) diff --git a/CefSharp.Wpf/IWpfChromiumWebBrowser.cs b/CefSharp.Wpf/IWpfChromiumWebBrowser.cs new file mode 100644 index 0000000000..59626bc576 --- /dev/null +++ b/CefSharp.Wpf/IWpfChromiumWebBrowser.cs @@ -0,0 +1,151 @@ +// Copyright © 2013 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; +using System.Windows; +using System.Windows.Input; +using System.Windows.Threading; +using CefSharp.Enums; +using CefSharp.Internals; + +namespace CefSharp.Wpf +{ + /// + /// WPF specific implementation, has reference to some of the commands + /// and properties the exposes. + /// + /// + public interface IWpfChromiumWebBrowser : IWebBrowser, IInputElement + { + /// + /// Navigates to the previous page in the browser history. Will automatically be enabled/disabled depending on the + /// browser state. + /// + /// The back command. + ICommand BackCommand { get; } + + /// + /// Navigates to the next page in the browser history. Will automatically be enabled/disabled depending on the + /// browser state. + /// + /// The forward command. + ICommand ForwardCommand { get; } + + /// + /// Reloads the content of the current page. Will automatically be enabled/disabled depending on the browser state. + /// + /// The reload command. + ICommand ReloadCommand { get; } + + /// + /// Prints the current browser contents. + /// + /// The print command. + ICommand PrintCommand { get; } + + /// + /// Increases the zoom level. + /// + /// The zoom in command. + ICommand ZoomInCommand { get; } + + /// + /// Decreases the zoom level. + /// + /// The zoom out command. + ICommand ZoomOutCommand { get; } + + /// + /// Resets the zoom level to the default. (100%) + /// + /// The zoom reset command. + ICommand ZoomResetCommand { get; } + + /// + /// Opens up a new program window (using the default text editor) where the source code of the currently displayed web + /// page is shown. + /// + /// The view source command. + ICommand ViewSourceCommand { get; } + + /// + /// Command which cleans up the Resources used by the ChromiumWebBrowser + /// + /// The cleanup command. + ICommand CleanupCommand { get; } + + /// + /// Stops loading the current page. + /// + /// The stop command. + ICommand StopCommand { get; } + + /// + /// Cut selected text to the clipboard. + /// + /// The cut command. + ICommand CutCommand { get; } + + /// + /// Copy selected text to the clipboard. + /// + /// The copy command. + ICommand CopyCommand { get; } + + /// + /// Paste text from the clipboard. + /// + /// The paste command. + ICommand PasteCommand { get; } + + /// + /// Select all text. + /// + /// The select all command. + ICommand SelectAllCommand { get; } + + /// + /// Undo last action. + /// + /// The undo command. + ICommand UndoCommand { get; } + + /// + /// Redo last action. + /// + /// The redo command. + ICommand RedoCommand { get; } + + /// + /// Toggles the audio mute for the current browser. + /// + ICommand ToggleAudioMuteCommand { get; } + + /// + /// Gets the associated with this instance. + /// + /// The dispatcher. + Dispatcher Dispatcher { get; } + + /// + /// The zoom level at which the browser control is currently displaying. + /// Can be set to 0 to clear the zoom level (resets to default zoom level). + /// + /// The zoom level. + double ZoomLevel { get; set; } + + /// + /// The increment at which the property will be incremented/decremented. + /// + /// The zoom level increment. + double ZoomLevelIncrement { get; set; } + + /// + /// The title of the web page being currently displayed. + /// + /// The title. + /// This property is implemented as a Dependency Property and fully supports data binding. + string Title { get; } + } +} diff --git a/CefSharp.Wpf/IWpfWebBrowser.cs b/CefSharp.Wpf/IWpfWebBrowser.cs index 0fb0c0e4c1..ec0966eb69 100644 --- a/CefSharp.Wpf/IWpfWebBrowser.cs +++ b/CefSharp.Wpf/IWpfWebBrowser.cs @@ -16,138 +16,8 @@ namespace CefSharp.Wpf /// and properties the exposes. /// /// - public interface IWpfWebBrowser : IWebBrowser, IInputElement + public interface IWpfWebBrowser : IWpfChromiumWebBrowser, IInputElement { - /// - /// Navigates to the previous page in the browser history. Will automatically be enabled/disabled depending on the - /// browser state. - /// - /// The back command. - ICommand BackCommand { get; } - - /// - /// Navigates to the next page in the browser history. Will automatically be enabled/disabled depending on the - /// browser state. - /// - /// The forward command. - ICommand ForwardCommand { get; } - - /// - /// Reloads the content of the current page. Will automatically be enabled/disabled depending on the browser state. - /// - /// The reload command. - ICommand ReloadCommand { get; } - - /// - /// Prints the current browser contents. - /// - /// The print command. - ICommand PrintCommand { get; } - - /// - /// Increases the zoom level. - /// - /// The zoom in command. - ICommand ZoomInCommand { get; } - - /// - /// Decreases the zoom level. - /// - /// The zoom out command. - ICommand ZoomOutCommand { get; } - - /// - /// Resets the zoom level to the default. (100%) - /// - /// The zoom reset command. - ICommand ZoomResetCommand { get; } - - /// - /// Opens up a new program window (using the default text editor) where the source code of the currently displayed web - /// page is shown. - /// - /// The view source command. - ICommand ViewSourceCommand { get; } - - /// - /// Command which cleans up the Resources used by the ChromiumWebBrowser - /// - /// The cleanup command. - ICommand CleanupCommand { get; } - - /// - /// Stops loading the current page. - /// - /// The stop command. - ICommand StopCommand { get; } - - /// - /// Cut selected text to the clipboard. - /// - /// The cut command. - ICommand CutCommand { get; } - - /// - /// Copy selected text to the clipboard. - /// - /// The copy command. - ICommand CopyCommand { get; } - - /// - /// Paste text from the clipboard. - /// - /// The paste command. - ICommand PasteCommand { get; } - - /// - /// Select all text. - /// - /// The select all command. - ICommand SelectAllCommand { get; } - - /// - /// Undo last action. - /// - /// The undo command. - ICommand UndoCommand { get; } - - /// - /// Redo last action. - /// - /// The redo command. - ICommand RedoCommand { get; } - - /// - /// Toggles the audio mute for the current browser. - /// - ICommand ToggleAudioMuteCommand { get; } - - /// - /// Gets the associated with this instance. - /// - /// The dispatcher. - Dispatcher Dispatcher { get; } - - /// - /// The zoom level at which the browser control is currently displaying. - /// Can be set to 0 to clear the zoom level (resets to default zoom level). - /// - /// The zoom level. - double ZoomLevel { get; set; } - - /// - /// The increment at which the property will be incremented/decremented. - /// - /// The zoom level increment. - double ZoomLevelIncrement { get; set; } - - /// - /// The title of the web page being currently displayed. - /// - /// The title. - /// This property is implemented as a Dependency Property and fully supports data binding. - string Title { get; } - /// /// Raised every time is called. You can access the underlying buffer, though it's /// preferable to either override or implement your own as there is no outwardly diff --git a/CefSharp.Wpf/DelegateCommand.cs b/CefSharp.Wpf/Internals/DelegateCommand.cs similarity index 91% rename from CefSharp.Wpf/DelegateCommand.cs rename to CefSharp.Wpf/Internals/DelegateCommand.cs index ba4002a459..abf505f57b 100644 --- a/CefSharp.Wpf/DelegateCommand.cs +++ b/CefSharp.Wpf/Internals/DelegateCommand.cs @@ -5,12 +5,12 @@ using System; using System.Windows.Input; -namespace CefSharp.Wpf +namespace CefSharp.Wpf.Internals { /// /// DelegateCommand /// - /// + /// internal class DelegateCommand : ICommand { /// @@ -64,10 +64,7 @@ public bool CanExecute(object parameter) /// public void RaiseCanExecuteChanged() { - if (CanExecuteChanged != null) - { - CanExecuteChanged(this, EventArgs.Empty); - } + CanExecuteChanged?.Invoke(this, EventArgs.Empty); } } } diff --git a/CefSharp.Wpf/Internals/IMousePositionTransform.cs b/CefSharp.Wpf/Internals/IMousePositionTransform.cs new file mode 100644 index 0000000000..4140bcab1f --- /dev/null +++ b/CefSharp.Wpf/Internals/IMousePositionTransform.cs @@ -0,0 +1,14 @@ +using CefSharp.Structs; + +namespace CefSharp.Wpf.Internals +{ + /// + /// Implement this interface to control transform the mouse position + /// + public interface IMousePositionTransform + { + System.Windows.Point UpdatePopupSizeAndPosition(Rect originalRect, Rect viewRect); + void OnPopupShow(bool isOpen); + void TransformMousePoint(ref System.Windows.Point point); + } +} diff --git a/CefSharp.Wpf/Internals/MousePositionTransform.cs b/CefSharp.Wpf/Internals/MousePositionTransform.cs new file mode 100644 index 0000000000..419bafd692 --- /dev/null +++ b/CefSharp.Wpf/Internals/MousePositionTransform.cs @@ -0,0 +1,153 @@ +using CefSharp.Structs; + +namespace CefSharp.Wpf.Internals +{ + public sealed class MousePositionTransform : IMousePositionTransform + { + /// + /// The x-offset. + /// + private int xOffset; + + /// + /// The y-offset. + /// + private int yOffset; + + /// + /// The original rect. + /// + private Rect originalRect; + + /// + /// The adjusted rect. + /// + private Rect adjustedRect; + + /// + /// If the popup is open or not. + /// + private bool isOpen; + + /// + /// Updates the size and the position of the popup. + /// + /// + /// + /// The adjusted point. + System.Windows.Point IMousePositionTransform.UpdatePopupSizeAndPosition(Rect originalRect, Rect viewRect) + { + int x = originalRect.X, + prevX = originalRect.X, + y = originalRect.Y, + prevY = originalRect.Y, + xOffset = 0, + yOffset = 0; + + // If popup goes outside the view, try to reposition origin + if (originalRect.X + originalRect.Width > viewRect.Width) + { + x = viewRect.Width - originalRect.Width; + xOffset = prevX - x; + } + + if (originalRect.Y + originalRect.Height > viewRect.Height) + { + y = y - originalRect.Height - 20; + yOffset = prevY - y; + } + + // If x or y became negative, move them to 0 again + if (x < 0) + { + x = 0; + xOffset = prevX; + } + + if (y < 0) + { + y = 0; + yOffset = prevY; + } + + if (x != prevX || y != prevY) + { + this.isOpen = true; + + this.xOffset = xOffset; + this.yOffset = yOffset; + + Rect adjustedRect = new Rect(x, y, x + originalRect.Width, y + originalRect.Height); + + this.originalRect = originalRect; + this.adjustedRect = adjustedRect; + + if (this.originalRect.Y < this.adjustedRect.Y + this.adjustedRect.Height) + { + var newY = this.adjustedRect.Y + this.adjustedRect.Height; + this.originalRect = new Rect(originalRect.X, newY, originalRect.Width, originalRect.Y + originalRect.Height - newY); + } + } + + return new System.Windows.Point(x, y); + } + + /// + /// Resets the offsets and original-rect. + /// If the popup is open or not. + /// + void IMousePositionTransform.OnPopupShow(bool isOpen) + { + if (!isOpen) + { + this.isOpen = false; + + this.xOffset = 0; + this.yOffset = 0; + + this.originalRect = new Rect(); + this.originalRect = new Rect(); + } + } + + /// + /// Adjusts the mouse-coordinates when the popup is visible. + /// + /// The original point. + /// The transformed point if needed, else the original point. + void IMousePositionTransform.TransformMousePoint(ref System.Windows.Point point) + { + if (!isOpen) + return; + + if (!IsInsideOriginalRect(point) && IsInsideAdjustedRect(point)) + point = new System.Windows.Point((int)point.X + this.xOffset, (int)point.Y + this.yOffset); + } + + /// + /// Checks if the given point is inside the original-rect. + /// + /// The point. + /// Returns true if the point is inside the original rect, else return false. + private bool IsInsideOriginalRect(System.Windows.Point point) + { + return point.X >= this.originalRect.X && + point.X < this.originalRect.X + this.originalRect.Width && + point.Y >= this.originalRect.Y && + point.Y < this.originalRect.Y + this.originalRect.Height; + } + + /// + /// Checks if the given point is inside the adjusted rect. + /// + /// The point. + /// Returns true if the point is inside the adjusted rect, else return false. + private bool IsInsideAdjustedRect(System.Windows.Point point) + { + return point.X >= this.adjustedRect.X && + point.X < this.adjustedRect.X + this.adjustedRect.Width && + point.Y >= this.adjustedRect.Y && + point.Y < this.adjustedRect.Y + this.adjustedRect.Height; + } + } +} diff --git a/CefSharp.Wpf/Internals/NoOpMousePositionTransform.cs b/CefSharp.Wpf/Internals/NoOpMousePositionTransform.cs new file mode 100644 index 0000000000..9a271ec6b1 --- /dev/null +++ b/CefSharp.Wpf/Internals/NoOpMousePositionTransform.cs @@ -0,0 +1,21 @@ +using CefSharp.Structs; + +namespace CefSharp.Wpf.Internals +{ + public sealed class NoOpMousePositionTransform : IMousePositionTransform + { + System.Windows.Point IMousePositionTransform.UpdatePopupSizeAndPosition(Rect originalRect, Rect viewRect) + { + return new System.Windows.Point(originalRect.X, originalRect.Y); + } + + void IMousePositionTransform.OnPopupShow(bool isOpen) + { + } + + void IMousePositionTransform.TransformMousePoint(ref System.Windows.Point point) + { + + } + } +} diff --git a/CefSharp.Wpf/Rendering/AbstractRenderHandler.cs b/CefSharp.Wpf/Rendering/AbstractRenderHandler.cs index aea6f850c3..d355a1edc6 100644 --- a/CefSharp.Wpf/Rendering/AbstractRenderHandler.cs +++ b/CefSharp.Wpf/Rendering/AbstractRenderHandler.cs @@ -98,14 +98,8 @@ protected void ReleaseMemoryMappedView(ref MemoryMappedFile mappedFile, ref Memo } } - /// - /// Called when an element has been rendered to the shared texture handle. - /// This method is only called when is set to true - /// - /// indicates whether the element is the view or the popup widget. - /// contains the set of rectangles in pixel coordinates that need to be repainted - /// is the handle for a D3D11 Texture2D that can be accessed via ID3D11Device using the OpenSharedResource method. - public virtual void OnAcceleratedPaint(bool isPopup, Rect dirtyRect, IntPtr sharedHandle) + /// + public virtual void OnAcceleratedPaint(bool isPopup, Rect dirtyRect, AcceleratedPaintInfo acceleratedPaintInfo) { // NOT USED } diff --git a/CefSharp.Wpf/Rendering/AllocHGlobalWritableBitmapRenderHandler.cs b/CefSharp.Wpf/Rendering/AllocHGlobalWritableBitmapRenderHandler.cs index 0ef696b897..58ffa6761f 100644 --- a/CefSharp.Wpf/Rendering/AllocHGlobalWritableBitmapRenderHandler.cs +++ b/CefSharp.Wpf/Rendering/AllocHGlobalWritableBitmapRenderHandler.cs @@ -96,7 +96,8 @@ protected virtual void Dispose(bool disposing) } } - void IRenderHandler.OnAcceleratedPaint(bool isPopup, Rect dirtyRect, IntPtr sharedHandle) + /// + void IRenderHandler.OnAcceleratedPaint(bool isPopup, Rect dirtyRect, AcceleratedPaintInfo acceleratedPaintInfo) { //NOT USED } diff --git a/CefSharp.Wpf/Rendering/DirectWritableBitmapRenderHandler.cs b/CefSharp.Wpf/Rendering/DirectWritableBitmapRenderHandler.cs index 27c8354a27..c38b883ee4 100644 --- a/CefSharp.Wpf/Rendering/DirectWritableBitmapRenderHandler.cs +++ b/CefSharp.Wpf/Rendering/DirectWritableBitmapRenderHandler.cs @@ -42,16 +42,19 @@ public DirectWritableBitmapRenderHandler(double dpiX, double dpiY, bool invalida this.invalidateDirtyRect = invalidateDirtyRect; } + /// void IDisposable.Dispose() { } - void IRenderHandler.OnAcceleratedPaint(bool isPopup, Rect dirtyRect, IntPtr sharedHandle) + /// + void IRenderHandler.OnAcceleratedPaint(bool isPopup, Rect dirtyRect, AcceleratedPaintInfo acceleratedPaintInfo) { throw new NotImplementedException(); } + /// void IRenderHandler.OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image) { var writeableBitmap = image.Source as WriteableBitmap; diff --git a/CefSharp.Wpf/Rendering/Experimental/ByteArrayWritableBitmapRenderHandler.cs b/CefSharp.Wpf/Rendering/Experimental/ByteArrayWritableBitmapRenderHandler.cs index ac29271a20..8f3afe6d1c 100644 --- a/CefSharp.Wpf/Rendering/Experimental/ByteArrayWritableBitmapRenderHandler.cs +++ b/CefSharp.Wpf/Rendering/Experimental/ByteArrayWritableBitmapRenderHandler.cs @@ -42,14 +42,8 @@ public ByteArrayWritableBitmapRenderHandler(double dpiX, double dpiY, bool inval this.dispatcherPriority = dispatcherPriority; } - /// - /// Called when an element has been rendered to the shared texture handle. - /// This method is only called when is set to true - /// - /// indicates whether the element is the view or the popup widget. - /// contains the set of rectangles in pixel coordinates that need to be repainted - /// is the handle for a D3D11 Texture2D that can be accessed via ID3D11Device using the OpenSharedResource method. - void IRenderHandler.OnAcceleratedPaint(bool isPopup, Rect dirtyRect, IntPtr sharedHandle) + /// + void IRenderHandler.OnAcceleratedPaint(bool isPopup, Rect dirtyRect, AcceleratedPaintInfo acceleratedPaintInfo) { //NOT USED } diff --git a/CefSharp.Wpf/Rendering/Experimental/CompositionTargetRenderHandler.cs b/CefSharp.Wpf/Rendering/Experimental/CompositionTargetRenderHandler.cs index c4dd852b53..b11451498e 100644 --- a/CefSharp.Wpf/Rendering/Experimental/CompositionTargetRenderHandler.cs +++ b/CefSharp.Wpf/Rendering/Experimental/CompositionTargetRenderHandler.cs @@ -59,6 +59,7 @@ private void BrowserIsVisibleChanged(object sender, DependencyPropertyChangedEve } } + /// void IDisposable.Dispose() { CompositionTarget.Rendering -= OnRendering; @@ -78,11 +79,13 @@ void IDisposable.Dispose() } } - void IRenderHandler.OnAcceleratedPaint(bool isPopup, Rect dirtyRect, IntPtr sharedHandle) + /// + void IRenderHandler.OnAcceleratedPaint(bool isPopup, Rect dirtyRect, AcceleratedPaintInfo acceleratedPaintInfo) { throw new NotImplementedException(); } + /// void IRenderHandler.OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image) { if (image.Dispatcher.HasShutdownStarted) diff --git a/CefSharp.Wpf/Rendering/WritableBitmapRenderHandler.cs b/CefSharp.Wpf/Rendering/WritableBitmapRenderHandler.cs index b0f14f2f63..204e018e8a 100644 --- a/CefSharp.Wpf/Rendering/WritableBitmapRenderHandler.cs +++ b/CefSharp.Wpf/Rendering/WritableBitmapRenderHandler.cs @@ -134,7 +134,7 @@ protected override void CreateOrUpdateBitmap(bool isPopup, Rect dirtyRect, IntPt return; } - if (createNewBitmap) + if (createNewBitmap || image.Source is null) { if (image.Source != null) { @@ -160,6 +160,14 @@ protected override void CreateOrUpdateBitmap(bool isPopup, Rect dirtyRect, IntPt return; } + var sourceBufferPtr = sourceBuffer.DangerousGetHandle(); + + // Issue https://github.com/cefsharp/CefSharp/issues/4426 + if (sourceBufferPtr == IntPtr.Zero) + { + return; + } + //By default we'll only update the dirty rect, for those that run into a MILERR_WIN32ERROR Exception (#2035) //it's desirably to either upgrade to a newer .Net version (only client runtime needs to be installed, not compiled //against a newer version. Or invalidate the whole bitmap @@ -169,7 +177,7 @@ protected override void CreateOrUpdateBitmap(bool isPopup, Rect dirtyRect, IntPt var sourceRect = new Int32Rect(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); bitmap.Lock(); - bitmap.WritePixels(sourceRect, sourceBuffer.DangerousGetHandle(), noOfBytes, stride, dirtyRect.X, dirtyRect.Y); + bitmap.WritePixels(sourceRect, sourceBufferPtr, noOfBytes, stride, dirtyRect.X, dirtyRect.Y); bitmap.Unlock(); } else @@ -178,7 +186,7 @@ protected override void CreateOrUpdateBitmap(bool isPopup, Rect dirtyRect, IntPt var sourceRect = new Int32Rect(0, 0, width, height); bitmap.Lock(); - bitmap.WritePixels(sourceRect, sourceBuffer.DangerousGetHandle(), noOfBytes, stride); + bitmap.WritePixels(sourceRect, sourceBufferPtr, noOfBytes, stride); bitmap.Unlock(); } } diff --git a/CefSharp.shfbproj b/CefSharp.shfbproj index 00c06d936f..0ea75c3185 100644 --- a/CefSharp.shfbproj +++ b/CefSharp.shfbproj @@ -13,7 +13,7 @@ Documentation Documentation - .NET Framework 4.5.2 + .NET Framework 4.6.2 .\Help\ Documentation en-US @@ -31,7 +31,7 @@ - 110.0.220 + 138.0.330 2 False C#, Managed C++ @@ -59,7 +59,7 @@ InheritedMembers, InheritedFrameworkMembers, Protected, ProtectedInternalAsProtected, EditorBrowsableNever, NonBrowsable - Version 110.0.220 + Version 138.0.330 https://raw.githubusercontent.com/cefsharp/CefSharp/master/LICENSE Interfaces, enums, structs and classes that make up the core API interface diff --git a/CefSharp/AcceleratedPaintInfo.cs b/CefSharp/AcceleratedPaintInfo.cs new file mode 100644 index 0000000000..586105fda3 --- /dev/null +++ b/CefSharp/AcceleratedPaintInfo.cs @@ -0,0 +1,37 @@ +// Copyright © 2024 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; +using CefSharp.Enums; + +namespace CefSharp +{ + /// + /// Class representing accelerated paint info. + /// + public sealed class AcceleratedPaintInfo + { + /// + /// Handle for the shared texture. The shared texture is instantiated + /// without a keyed mutex. + /// + public IntPtr SharedTextureHandle { get; } + + /// + /// The pixel format of the texture. + /// + public ColorType Format { get; } + + /// + /// AcceleratedPaintInfo + /// + /// Handle to the shared texture resource + /// The pixel format of the shared texture resource + public AcceleratedPaintInfo(IntPtr sharedTextureHandle, ColorType format) + { + SharedTextureHandle = sharedTextureHandle; + Format = format; + } + } +} diff --git a/CefSharp/Callback/IGetExtensionResourceCallback.cs b/CefSharp/Callback/IGetExtensionResourceCallback.cs deleted file mode 100644 index 23ea1d8579..0000000000 --- a/CefSharp/Callback/IGetExtensionResourceCallback.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright © 2018 The CefSharp Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. - -using System; -using System.IO; - -namespace CefSharp -{ - /// - /// Callback interface used for asynchronous continuation of . - /// - public interface IGetExtensionResourceCallback : IDisposable - { - /// - /// Continue the request. Read the resource contents from stream. - /// - /// stream to be used as response. - void Continue(Stream stream); - - /// - /// Continue the request - /// - /// data to be used as response - void Continue(byte[] data); - - /// - /// Cancel the request. - /// - void Cancel(); - } -} diff --git a/CefSharp/Callback/IMediaAccessCallback.cs b/CefSharp/Callback/IMediaAccessCallback.cs index 8ef49fbc74..fc40c2b66d 100644 --- a/CefSharp/Callback/IMediaAccessCallback.cs +++ b/CefSharp/Callback/IMediaAccessCallback.cs @@ -16,7 +16,7 @@ public interface IMediaAccessCallback : IDisposable /// Call to allow or deny media access. If this callback was initiated in /// response to a getUserMedia (indicated by /// DeviceAudioCapture and/or DeviceVideoCapture being set) then - /// must match passed to + /// must match requestedPermissions param passed to /// /// /// Allowed Permissions diff --git a/CefSharp/Callback/TaskCompletionCallback.cs b/CefSharp/Callback/TaskCompletionCallback.cs index 56428f4ce7..c930749daf 100644 --- a/CefSharp/Callback/TaskCompletionCallback.cs +++ b/CefSharp/Callback/TaskCompletionCallback.cs @@ -4,7 +4,6 @@ using System; using System.Threading.Tasks; -using CefSharp.Internals; namespace CefSharp { @@ -22,14 +21,14 @@ public sealed class TaskCompletionCallback : ICompletionCallback /// public TaskCompletionCallback() { - taskCompletionSource = new TaskCompletionSource(); + taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); } void ICompletionCallback.OnComplete() { onComplete = true; - taskCompletionSource.TrySetResultAsync(true); + taskCompletionSource.TrySetResult(true); } /// @@ -50,11 +49,10 @@ void IDisposable.Dispose() var task = taskCompletionSource.Task; //If onComplete is false then ICompletionCallback.OnComplete was never called, - //so we'll set the result to false. Calling TrySetResultAsync multiple times - //can result in the issue outlined in https://github.com/cefsharp/CefSharp/pull/2349 + //so we'll set the result to false. if (onComplete == false && task.IsCompleted == false) { - taskCompletionSource.TrySetResultAsync(false); + taskCompletionSource.TrySetResult(false); } isDisposed = true; diff --git a/CefSharp/Callback/TaskDeleteCookiesCallback.cs b/CefSharp/Callback/TaskDeleteCookiesCallback.cs index 13545fdf9a..cc7fc12011 100644 --- a/CefSharp/Callback/TaskDeleteCookiesCallback.cs +++ b/CefSharp/Callback/TaskDeleteCookiesCallback.cs @@ -27,14 +27,14 @@ public class TaskDeleteCookiesCallback : IDeleteCookiesCallback /// public TaskDeleteCookiesCallback() { - taskCompletionSource = new TaskCompletionSource(); + taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); } void IDeleteCookiesCallback.OnComplete(int numDeleted) { onComplete = true; - taskCompletionSource.TrySetResultAsync(numDeleted); + taskCompletionSource.TrySetResult(numDeleted); } /// @@ -54,11 +54,10 @@ void IDisposable.Dispose() var task = taskCompletionSource.Task; //If onComplete is false then IDeleteCookiesCallback.OnComplete was never called, - //so we'll set the result to false. Calling TrySetResultAsync multiple times - //can result in the issue outlined in https://github.com/cefsharp/CefSharp/pull/2349 + //so we'll set the result to false. if (onComplete == false && task.IsCompleted == false) { - taskCompletionSource.TrySetResultAsync(InvalidNoOfCookiesDeleted); + taskCompletionSource.TrySetResult(InvalidNoOfCookiesDeleted); } isDisposed = true; diff --git a/CefSharp/Callback/TaskPrintToPdfCallback.cs b/CefSharp/Callback/TaskPrintToPdfCallback.cs index dcf1d1a5f6..18bc66ac2c 100644 --- a/CefSharp/Callback/TaskPrintToPdfCallback.cs +++ b/CefSharp/Callback/TaskPrintToPdfCallback.cs @@ -13,7 +13,7 @@ namespace CefSharp /// public sealed class TaskPrintToPdfCallback : IPrintToPdfCallback { - private readonly TaskCompletionSource taskCompletionSource = new TaskCompletionSource(); + private readonly TaskCompletionSource taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); private volatile bool isDisposed; private bool onComplete; //Only ever accessed on the same CEF thread, so no need for thread safety @@ -29,7 +29,7 @@ void IPrintToPdfCallback.OnPdfPrintFinished(string path, bool ok) { onComplete = true; - taskCompletionSource.TrySetResultAsync(ok); + taskCompletionSource.TrySetResult(ok); } bool IPrintToPdfCallback.IsDisposed @@ -42,11 +42,10 @@ void IDisposable.Dispose() var task = taskCompletionSource.Task; //If onComplete is false then IPrintToPdfCallback.OnPdfPrintFinished was never called, - //so we'll set the result to false. Calling TrySetResultAsync multiple times - //can result in the issue outlined in https://github.com/cefsharp/CefSharp/pull/2349 + //so we'll set the result to false. if (onComplete == false && task.IsCompleted == false) { - taskCompletionSource.TrySetResultAsync(false); + taskCompletionSource.TrySetResult(false); } isDisposed = true; diff --git a/CefSharp/Callback/TaskResolveCallback.cs b/CefSharp/Callback/TaskResolveCallback.cs index 1ea00e2c8c..ccbf0e1467 100644 --- a/CefSharp/Callback/TaskResolveCallback.cs +++ b/CefSharp/Callback/TaskResolveCallback.cs @@ -23,14 +23,14 @@ public sealed class TaskResolveCallback : IResolveCallback /// public TaskResolveCallback() { - taskCompletionSource = new TaskCompletionSource(); + taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); } void IResolveCallback.OnResolveCompleted(CefErrorCode result, IList resolvedIpAddresses) { onComplete = true; - taskCompletionSource.TrySetResultAsync(new ResolveCallbackResult(result, resolvedIpAddresses)); + taskCompletionSource.TrySetResult(new ResolveCallbackResult(result, resolvedIpAddresses)); } bool IResolveCallback.IsDisposed @@ -51,11 +51,10 @@ void IDisposable.Dispose() var task = taskCompletionSource.Task; //If onComplete is false then IResolveCallback.OnResolveCompleted was never called, - //so we'll set the result to false. Calling TrySetResultAsync multiple times - //can result in the issue outlined in https://github.com/cefsharp/CefSharp/pull/2349 + //so we'll set the result to false. if (onComplete == false && task.IsCompleted == false) { - taskCompletionSource.TrySetResultAsync(new ResolveCallbackResult(CefErrorCode.Unexpected, null)); + taskCompletionSource.TrySetResult(new ResolveCallbackResult(CefErrorCode.Unexpected, null)); } isDisposed = true; diff --git a/CefSharp/Callback/TaskSetCookieCallback.cs b/CefSharp/Callback/TaskSetCookieCallback.cs index dffcaaae6d..28126ca502 100644 --- a/CefSharp/Callback/TaskSetCookieCallback.cs +++ b/CefSharp/Callback/TaskSetCookieCallback.cs @@ -22,14 +22,14 @@ public sealed class TaskSetCookieCallback : ISetCookieCallback ///
public TaskSetCookieCallback() { - taskCompletionSource = new TaskCompletionSource(); + taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); } void ISetCookieCallback.OnComplete(bool success) { onComplete = true; - taskCompletionSource.TrySetResultAsync(success); + taskCompletionSource.TrySetResult(success); } /// @@ -50,11 +50,10 @@ void IDisposable.Dispose() var task = taskCompletionSource.Task; //If onComplete is false then ISetCookieCallback.OnComplete was never called, - //so we'll set the result to false. Calling TrySetResultAsync multiple times - //can result in the issue outlined in https://github.com/cefsharp/CefSharp/pull/2349 + //so we'll set the result to false. if (onComplete == false && task.IsCompleted == false) { - taskCompletionSource.TrySetResultAsync(false); + taskCompletionSource.TrySetResult(false); } isDisposed = true; diff --git a/CefSharp/CefSharp.csproj b/CefSharp/CefSharp.csproj index 4db7f4767a..e9103e6517 100644 --- a/CefSharp/CefSharp.csproj +++ b/CefSharp/CefSharp.csproj @@ -1,6 +1,6 @@ - net452 + net462 Library false true @@ -22,13 +22,12 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/CefSharp/CefSharp.netcore.csproj b/CefSharp/CefSharp.netcore.csproj index 7bf3764cdd..c44373914f 100644 --- a/CefSharp/CefSharp.netcore.csproj +++ b/CefSharp/CefSharp.netcore.csproj @@ -10,7 +10,7 @@ - netcoreapp3.1 + net6.0 CefSharp CefSharp $(BaseOutputPath)$(Configuration)\$(TargetFramework)\$(AssemblyName).xml @@ -22,16 +22,16 @@ true + true True embedded - + all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/CefSharp/CefSharpSettings.cs b/CefSharp/CefSharpSettings.cs index 9b18652fad..d5ca8754c6 100644 --- a/CefSharp/CefSharpSettings.cs +++ b/CefSharp/CefSharpSettings.cs @@ -81,5 +81,19 @@ static CefSharpSettings() /// false. /// public static bool FocusedNodeChangedEnabled { get; set; } + + /// + /// CefSharp.WinForms and CefSharp.Wpf.HwndHost ONLY! + /// The default is to create + /// styled browsers, those wanting to use + /// styled browsers can set this property. + /// + /// + /// Sets the property. + /// You can customise this yourself on a per browser basis + /// by overriding the ChromiumWebBrowser.CreateBrowserWindowInfo + /// property. + /// + public static CefRuntimeStyle? RuntimeStyle { get; set; } } } diff --git a/CefSharp/DependencyChecker.cs b/CefSharp/DependencyChecker.cs index 79475ac79b..41ea796cc6 100644 --- a/CefSharp/DependencyChecker.cs +++ b/CefSharp/DependencyChecker.cs @@ -5,13 +5,7 @@ using System; using System.Collections.Generic; using System.IO; -#if NETCOREAPP -using System.Linq; -#endif using System.Reflection; -#if NETCOREAPP -using System.Runtime.InteropServices; -#endif using System.Text; namespace CefSharp @@ -27,11 +21,6 @@ public static class DependencyChecker ///
public const string LocalesPackFile = @"locales\en-US.pak"; - /// - /// File name of the Direct3D Compiler DLL. - /// - private const string D3DCompilerDll = "d3dcompiler_47.dll"; - /// /// List of Cef Dependencies /// @@ -46,7 +35,6 @@ public static class DependencyChecker // http://www.magpcss.org/ceforum/viewtopic.php?f=6&t=12580 // "natives_blob.bin" was removed // https://bugs.chromium.org/p/v8/issues/detail?id=7624#c60 - "snapshot_blob.bin", "v8_context_snapshot.bin" }; @@ -71,8 +59,7 @@ public static class DependencyChecker // Note: Without these components HTML5 accelerated content like 2D canvas, 3D CSS and WebGL will not function. "libEGL.dll", "libGLESv2.dll", - // The D3D Compiler isn't included in the win-arm64 redist; we remove it in the static constructor. - D3DCompilerDll, + "d3dcompiler_47.dll", //Crashpad support "chrome_elf.dll" }; @@ -111,17 +98,6 @@ public static class DependencyChecker #endif }; -#if NETCOREAPP - static DependencyChecker() - { - // win-arm64 doesn't ship with a copy of the D3D Compiler, it's included with the OS. - if (RuntimeInformation.ProcessArchitecture is Architecture.Arm64) - { - CefOptionalDependencies = CefOptionalDependencies.Where(x => x != D3DCompilerDll).ToArray(); - } - } -#endif - /// /// CheckDependencies iterates through the list of Cef and CefSharp dependencines /// relative to the path provided and returns a list of missing ones @@ -155,11 +131,7 @@ public static List CheckDependencies(bool checkOptional, bool packLoadin var missingDependencies = new List(); missingDependencies.AddRange(CheckDependencyList(nativeLibPath, CefDependencies)); - - if (!packLoadingDisabled) - { - missingDependencies.AddRange(CheckDependencyList(resourcesDirPath, CefResources)); - } + missingDependencies.AddRange(CheckDependencyList(resourcesDirPath, CefResources)); if (checkOptional) { diff --git a/CefSharp/Enums/CefErrorCode.cs b/CefSharp/Enums/CefErrorCode.cs index 1aa23348d4..7f5405cf8e 100644 --- a/CefSharp/Enums/CefErrorCode.cs +++ b/CefSharp/Enums/CefErrorCode.cs @@ -26,7 +26,7 @@ public enum CefErrorCode // 300-399 HTTP errors // 400-499 Cache errors // 500-599 ? - // 600-699 FTP errors + // 600-699 // 700-799 Certificate manager errors // 800-899 DNS resolver errors @@ -151,12 +151,7 @@ public enum CefErrorCode /// SocketIsConnected = -23, - /// - /// The request was blocked because the forced reenrollment check is still - /// pending. This error can only occur on ChromeOS. - /// The error can be emitted by code in chrome/browser/policy/policy_helpers.cc. - /// - BlockedEnrollmentCheckPending = -24, + // Error -24 was removed (BLOCKED_ENROLLMENT_CHECK_PENDING) /// /// The upload failed because the upload stream needed to be re-read, due to a @@ -200,6 +195,17 @@ public enum CefErrorCode /// BlockedByOrb = -32, + /// + /// The request was blocked because it originated from a frame that has disabled + /// network access. + /// + NetworkAccessRevoked = -33, + + /// + /// The request was blocked by fingerprinting protections. + /// + BlockedByFingerprintingProtection = -34, + /// /// A connection was closed (corresponding to a TCP FIN). /// @@ -409,7 +415,7 @@ public enum CefErrorCode /// received a 302 (temporary redirect) response. The response body might /// include a description of why the request failed. /// - /// TODO(https://crbug.com/928551): This is deprecated and should not be used by + /// TODO(crbug.com/40093955): This is deprecated and should not be used by /// new code. ///
HttpsProxyTunnelResponseRedirect = -140, @@ -788,11 +794,7 @@ public enum CefErrorCode ///
CertificateTransparencyRequired = -214, - /// - /// The certificate chained to a legacy Symantec root that is no longer trusted. - /// https://g.co/chrome/symantecpkicerts - /// - CertSymantecLegacy = -215, + // Error -215 was removed (CERT_SYMANTEC_LEGACY) // -216 was QUIC_CERT_ROOT_NOT_KNOWN which has been renumbered to not be in the // certificate error range. @@ -806,6 +808,12 @@ public enum CefErrorCode // -218 was SSL_OBSOLETE_VERSION which is not longer used. TLS 1.0/1.1 instead // cause SSL_VERSION_OR_CIPHER_MISMATCH now. + /// + /// The certificate is self signed and it's being used for either an RFC1918 IP + /// literal URL, or a url ending in .local. + /// + CertSelfSignedLocalNetwork = -219, + // Add new certificate error codes here. // // Update the value of CERT_END whenever you add a new certificate error @@ -814,7 +822,7 @@ public enum CefErrorCode /// /// The value immediately past the last certificate error code. /// - CertEnd = -219, + CertEnd = -220, /// /// The URL is invalid. @@ -1103,6 +1111,11 @@ public enum CefErrorCode /// PacScriptTerminated = -367, + /// + /// Signals that the request requires the IPP proxy. + /// + ProxyRequired = -368, + // Obsolete. Kept here to avoid reuse. // Request is throttled because of a Backoff header. // See: crbug.com/486891. @@ -1126,16 +1139,11 @@ public enum CefErrorCode /// Http2RstStreamNoErrorReceived = -372, - /// - /// The pushed stream claimed by the request is no longer available. - /// - Http2PushedStreamNotAvailable = -373, + // Obsolete. HTTP/2 push is removed. + // NET_ERROR(HTTP2_PUSHED_STREAM_NOT_AVAILABLE, -373) - /// - /// A pushed stream was claimed and later reset by the server. When this happens, - /// the request should be retried. - /// - Http2ClaimedPushedStreamResetByServer = -374, + // Obsolete. HTTP/2 push is removed. + // NET_ERROR(HTTP2_CLAIMED_PUSHED_STREAM_RESET_BY_SERVER, -374) /// /// An HTTP transaction was retried too many times due for authentication or @@ -1150,21 +1158,16 @@ public enum CefErrorCode /// Http2StreamClosed = -376, - /// - /// Client is refusing an HTTP/2 stream. - /// - Http2ClientRefusedStream = -377, + // Obsolete. HTTP/2 push is removed. + // NET_ERROR(HTTP2_CLIENT_REFUSED_STREAM, -377) - /// - /// A pushed HTTP/2 stream was claimed by a request based on matching URL and - /// request headers, but the pushed response headers do not match the request. - /// - Http2PushedResponseDoesNotMatch = -378, + // Obsolete. HTTP/2 push is removed. + // NET_ERROR(HTTP2_PUSHED_RESPONSE_DOES_NOT_MATCH, -378) /// /// The server returned a non-2xx HTTP response code. /// - /// Not that this error is only used by certain APIs that interpret the HTTP + /// Note that this error is only used by certain APIs that interpret the HTTP /// response itself. URLRequest for instance just passes most non-2xx /// response back as success. /// @@ -1201,6 +1204,26 @@ public enum CefErrorCode /// CachedIpAddressSpaceBlockedByPrivateNetworkAccessPolicy = -384, + /// + /// The connection is blocked by private network access checks. + /// + BlockedByPrivateNetworkAccessChecks = -385, + + /// + /// Content decoding failed due to the zstd window size being too big (over 8MB). + /// + ZstdWindowSizeTooBig = -386, + + /// + /// The compression dictionary cannot be loaded. + /// + DictionaryLoadFailed = -387, + + /// + /// The header of dictionary compressed stream does not match the expected value. + /// + UnexpectedContentDictionaryHeader = -388, + /// /// The cache does not have the requested entry. /// @@ -1324,51 +1347,13 @@ public enum CefErrorCode TrustTokenOperationSuccessWithoutSendingRequest = -507, // *** Code -600 is reserved (was FTP_PASV_COMMAND_FAILED). *** - - /// - /// A generic error for failed FTP control connection command. - /// If possible, please use or add a more specific error code. - /// - FtpFailed = -601, - - /// - /// The server cannot fulfill the request at this point. This is a temporary - /// error. - /// FTP response code 421. - /// - FtpServiceUnavailable = -602, - - /// - /// The server has aborted the transfer. - /// FTP response code 426. - /// - FtpTransferAborted = -603, - - /// - /// The file is busy, or some other temporary error condition on opening - /// the file. - /// FTP response code 450. - /// - FtpFileBusy = -604, - - /// - /// Server rejected our command because of syntax errors. - /// FTP response codes 500, 501. - /// - FtpSyntaxError = -605, - - /// - /// Server does not support the command we issued. - /// FTP response codes 502, 504. - /// - FtpCommandNotSupported = -606, - - /// - /// Server rejected our command because we didn't issue the commands in right - /// order. - /// FTP response code 503. - /// - FtpBadCommandSequence = -607, + // *** Code -601 is reserved (was FTP_FAILED). *** + // *** Code -602 is reserved (was FTP_SERVICE_UNAVAILABLE). *** + // *** Code -603 is reserved (was FTP_TRANSFER_ABORTED). *** + // *** Code -604 is reserved (was FTP_FILE_BUSY). *** + // *** Code -605 is reserved (was FTP_SYNTAX_ERROR). *** + // *** Code -606 is reserved (was FTP_COMMAND_NOT_SUPPORTED). *** + // *** Code -607 is reserved (was FTP_BAD_COMMAND_SEQUENCE). *** /// /// PKCS #12 import failed due to incorrect password. @@ -1441,6 +1426,11 @@ public enum CefErrorCode // Error -715 was removed (CHANNEL_ID_IMPORT_FAILED) + /// + /// The certificate verifier configuration changed in some way. + /// + CertVerifierChanged = -716, + // DNS error codes. /// @@ -1512,6 +1502,15 @@ public enum CefErrorCode /// The hostname resolution of HTTPS record was expected to be resolved with /// alpn values of supported protocols, but did not. /// - DnsNoMachingSupportedAlpn = -811, + DnsNoMatchingSupportedAlpn = -811, + + // Error -812 was removed + // Error -813 was removed + + /// + /// When checking whether secure DNS can be used, the response returned for the + /// requested probe record either had no answer or was invalid. + /// + DnsSecureProbeRecordInvalid = -814, }; } diff --git a/CefSharp/Enums/CefRuntimeStyle.cs b/CefSharp/Enums/CefRuntimeStyle.cs new file mode 100644 index 0000000000..50f2c67535 --- /dev/null +++ b/CefSharp/Enums/CefRuntimeStyle.cs @@ -0,0 +1,47 @@ +namespace CefSharp +{ + /// + /// CEF supports both a Chrome runtime (based on the Chrome UI layer) and an + /// Alloy runtime (based on the Chromium content layer). The Chrome runtime + /// provides the full Chrome UI and browser functionality whereas the Alloy + /// runtime provides less default browser functionality but adds additional + /// client callbacks and support for windowless (off-screen) rendering. For + /// additional comparative details on runtime types see + /// https://bitbucket.org/chromiumembedded/cef/wiki/Architecture.md#markdown-header-cef3 + /// + /// Each runtime is composed of a bootstrap component and a style component. + /// The style component is individually + /// configured for each window/browser at creation time and, in combination with + /// the Chrome bootstrap, different styles can be mixed during runtime. + /// + /// Windowless rendering will always use Alloy style. Windowed rendering with a + /// default window or client-provided parent window can configure the style via + /// CefWindowInfo.runtime_style. Windowed rendering with the Views framework can + /// configure the style via CefWindowDelegate::GetWindowRuntimeStyle and + /// CefBrowserViewDelegate::GetBrowserRuntimeStyle. Alloy style Windows with the + /// Views framework can host only Alloy style BrowserViews but Chrome style + /// Windows can host both style BrowserViews. Additionally, a Chrome style + /// Window can host at most one Chrome style BrowserView but potentially + /// multiple Alloy style BrowserViews. See CefWindowInfo.runtime_style + /// documentation for any additional platform-specific limitations. + /// + public enum CefRuntimeStyle + { + /// + /// Use the default runtime style. See above documentation + /// for exceptions. + /// + Default, + + /// + /// Use the Chrome runtime style. + /// + Chrome, + + /// + /// Use the Alloy runtime style. + /// runtime. + /// + Alloy, + } +} diff --git a/CefSharp/Enums/ContentSettingTypes.cs b/CefSharp/Enums/ContentSettingTypes.cs new file mode 100644 index 0000000000..666e73a2a2 --- /dev/null +++ b/CefSharp/Enums/ContentSettingTypes.cs @@ -0,0 +1,624 @@ +// Copyright © 2024 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +namespace CefSharp.Enums +{ + /// + /// Supported content setting types. Some types are platform-specific or only + /// supported with Chrome style. Should be kept in sync with Chromium's + /// ContentSettingsType type. + /// + public enum ContentSettingTypes + { + /// + /// This setting governs whether cookies are enabled by the user in the + /// provided context. However, it may be overridden by other settings. This + /// enum should NOT be read directly to determine whether cookies are enabled; + /// the client should instead rely on the CookieSettings API. + /// + Cookies = 0, + Images, + JavaScript, + + /// + /// This setting governs both popups and unwanted redirects like tab-unders + /// and framebusting. + /// + Popups, + + Geolocation, + Notifications, + AutoSelectCertificate, + MixedScript, + MediaStreamMic, + MediaStreamCamera, + ProtocolHandlers, + DeprecatedPpapiBroker, + AutomaticDownloads, + + /// + /// Advanced device-specific functions on MIDI devices. MIDI-SysEx + /// communications can be used for changing the MIDI device's persistent state + /// such as firmware. + /// + MidiSysex, + + SslCertDecisions, + ProtectedMediaIdentifier, + AppBanner, + SiteEngagement, + DurableStorage, + UsbChooserData, + BluetoothGuard, + BackgroundSync, + Autoplay, + ImportantSiteInfo, + PermissionAutoblockerData, + Ads, + + /// + /// Website setting which stores metadata for the subresource filter to aid in + /// decisions for whether or not to show the UI. + /// + AdsData, + + /// + /// MIDI stands for Musical Instrument Digital Interface. It is a standard + /// that allows electronic musical instruments, computers, and other devices + /// to communicate with each other. + /// + Midi, + + /// + /// This content setting type is for caching password protection service's + /// verdicts of each origin. + /// + PasswordProtection, + + /// + /// Website setting which stores engagement data for media related to a + /// specific origin. + /// + MediaEngagement, + + /// + /// Content setting which stores whether or not the site can play audible + /// sound. This will not block playback but instead the user will not hear it. + /// + Sound, + + /// + /// Website setting which stores the list of client hints that the origin + /// requested the browser to remember. The browser is expected to send all + /// client hints in the HTTP request headers for every resource requested + /// from that origin. + /// + ClientHints, + + /// + /// Generic Sensor API covering ambient-light-sensor, accelerometer, gyroscope + /// and magnetometer are all mapped to a single content_settings_type. + /// Setting for the Generic Sensor API covering ambient-light-sensor, + /// accelerometer, gyroscope and magnetometer. These are all mapped to a + /// single ContentSettingsType. + /// + Sensors, + + /// + /// Content setting which stores whether or not the user has granted the site + /// permission to respond to accessibility events, which can be used to + /// provide a custom accessibility experience. Requires explicit user consent + /// because some users may not want sites to know they're using assistive + /// technology. Deprecated in M131. + /// + DeprecatedAccessibilityEvents, + + /// + /// Used to store whether to allow a website to install a payment handler. + /// + PaymentHandler, + + /// + /// Content setting which stores whether to allow sites to ask for permission + /// to access USB devices. If this is allowed specific device permissions are + /// stored under USB_CHOOSER_DATA. + /// + UsbGuard, + + /// + /// Nothing is stored in this setting at present. Please refer to + /// BackgroundFetchPermissionContext for details on how this permission + /// is ascertained. + /// + BackgroundFetch, + + /// + /// Website setting which stores the amount of times the user has dismissed + /// intent picker UI without explicitly choosing an option. + /// + IntentPickerDisplay, + + /// + /// Used to store whether to allow a website to detect user active/idle state. + /// + IdleDetection, + + /// + /// Content settings for access to serial ports. The "guard" content setting + /// stores whether to allow sites to ask for permission to access a port. The + /// permissions granted to access particular ports are stored in the "chooser + /// data" website setting. + /// + SerialGuard, + SerialChooserData, + + /// + /// Nothing is stored in this setting at present. Please refer to + /// PeriodicBackgroundSyncPermissionContext for details on how this permission + /// is ascertained. + /// This content setting is not registered because it does not require access + /// to any existing providers. + /// + PeriodicBackgroundSync, + + /// + /// Content setting which stores whether to allow sites to ask for permission + /// to do Bluetooth scanning. + /// + BluetoothScanning, + + /// + /// Content settings for access to HID devices. The "guard" content setting + /// stores whether to allow sites to ask for permission to access a device. + /// The permissions granted to access particular devices are stored in the + /// "chooser data" website setting. + /// + HidGuard, + HidChooserData, + + /// + /// Wake Lock API, which has two lock types: screen and system locks. + /// Currently, screen locks do not need any additional permission, and system + /// locks are always denied while the right UI is worked out. + /// + WakeLockScreen, + WakeLockSystem, + + /// + /// Legacy SameSite cookie behavior. This disables SameSite=Lax-by-default, + /// SameSite=None requires Secure, and Schemeful Same-Site, forcing the + /// legacy behavior wherein 1) cookies that don't specify SameSite are treated + /// as SameSite=None, 2) SameSite=None cookies are not required to be Secure, + /// and 3) schemeful same-site is not active. + /// + /// This will also be used to revert to legacy behavior when future changes + /// in cookie handling are introduced. + /// + LegacyCookieAccess, + + /// + /// Content settings which stores whether to allow sites to ask for permission + /// to save changes to an original file selected by the user through the + /// File System Access API. + /// + FileSystemWriteGuard, + + /// + /// Used to store whether to allow a website to exchange data with NFC + /// devices. + /// + Nfc, + + /// + /// Website setting to store permissions granted to access particular + /// Bluetooth devices. + /// + BluetoothChooserData, + + /// + /// Full access to the system clipboard (sanitized read without user gesture, + /// and unsanitized read and write with user gesture). + /// + ClipboardReadWrite, + + /// + /// This is special-cased in the permissions layer to always allow, and as + /// such doesn't have associated prefs data. + /// + ClipboardSanitizedWrite, + + /// + /// This content setting type is for caching safe browsing real time url + /// check's verdicts of each origin. + /// + SafeBrowsingUrlCheckData, + + /// + /// Used to store whether a site is allowed to request AR or VR sessions with + /// the WebXr Device API. + /// + Vr, + Ar, + + /// + /// Content setting which stores whether to allow site to open and read files + /// and directories selected through the File System Access API. + /// + FileSystemReadGuard, + + /// + /// Access to first party storage in a third-party context. Exceptions are + /// scoped to the combination of requesting/top-level origin, and are managed + /// through the Storage Access API. For the time being, this content setting + /// exists in parallel to third-party cookie rules stored in COOKIES. + /// + StorageAccess, + + /// + /// Content setting which stores whether to allow a site to control camera + /// movements. It does not give access to camera. + /// + CameraPanTiltZoom, + + /// + /// Content setting for Screen Enumeration and Screen Detail functionality. + /// Permits access to detailed multi-screen information, like size and + /// position. Permits placing fullscreen and windowed content on specific + /// screens. See also: https://w3c.github.io/window-placement + /// + WindowManagement, + + /// + /// Stores whether to allow insecure websites to make private network + /// requests. + /// See also: https://wicg.github.io/cors-rfc1918 + /// Set through enterprise policies only. + /// + InsecurePrivateNetwork, + + /// + /// Content setting which stores whether or not a site can access low-level + /// locally installed font data using the Local Fonts Access API. + /// + LocalFonts, + + /// + /// Stores per-origin state for permission auto-revocation (for all permission + /// types). + /// + PermissionAutorevocationData, + + /// + /// Stores per-origin state of the most recently selected directory for the + /// use by the File System Access API. + /// + FileSystemLastPickedDirectory, + + /// + /// Controls access to the getDisplayMedia API. + /// + DisplayCapture, + + /// + /// Website setting to store permissions metadata granted to paths on the + /// local file system via the File System Access API. + /// |FILE_SYSTEM_WRITE_GUARD| is the corresponding "guard" setting. The stored + /// data represents valid permission only if + /// |FILE_SYSTEM_ACCESS_EXTENDED_PERMISSION| is enabled via user opt-in. + /// Otherwise, they represent "recently granted but revoked permission", which + /// are used to restore the permission. + /// + FileSystemAccessChooserData, + + /// + /// Stores a grant that allows a relying party to send a request for identity + /// information to specified identity providers, potentially through any + /// anti-tracking measures that would otherwise prevent it. This setting is + /// associated with the relying party's origin. + /// + FederatedIdentitySharing, + + /// + /// Whether to use the v8 optimized JIT for running JavaScript on the page. + /// + JavascriptJit, + + /// + /// Content setting which stores user decisions to allow loading a site over + /// HTTP. Entries are added by hostname when a user bypasses the HTTPS-First + /// Mode interstitial warning when a site does not support HTTPS. Allowed + /// hosts are exact hostname matches -- subdomains of a host on the allowlist + /// must be separately allowlisted. + /// + HttpAllowed, + + /// + /// Stores metadata related to form fill, such as e.g. whether user data was + /// autofilled on a specific website. + /// + FormfillMetadata, + + /// + /// Setting to indicate that there is an active federated sign-in session + /// between a specified relying party and a specified identity provider for + /// a specified account. When this is present it allows access to session + /// management capabilities between the sites. This setting is associated + /// with the relying party's origin. + /// Obsolete on Nov 2023. + /// + DeprecatedFederatedIdentityActiveSession, + + /// + /// Setting to indicate whether Chrome should automatically apply darkening to + /// web content. + /// + AutoDarkWebContent, + + /// + /// Setting to indicate whether Chrome should request the desktop view of a + /// site instead of the mobile one. + /// + RequestDesktopSite, + + /// + /// Setting to indicate whether browser should allow signing into a website + /// via the browser FedCM API. + /// + FederatedIdentityApi, + + /// + /// Stores notification interactions per origin for the past 90 days. + /// Interactions per origin are pre-aggregated over seven-day windows: A + /// notification interaction or display is assigned to the last Monday + /// midnight in local time. + /// + NotificationInteractions, + + /// + /// Website setting which stores the last reduced accept language negotiated + /// for a given origin, to be used on future visits to the origin. + /// + ReducedAcceptLanguage, + + /// + /// Website setting which is used for NotificationPermissionReviewService to + /// store origin blocklist from review notification permissions feature. + /// + NotificationPermissionReview, + + /// + /// Website setting to store permissions granted to access particular devices + /// in private network. + /// + PrivateNetworkGuard, + PrivateNetworkChooserData, + + /// + /// Website setting which stores whether the browser has observed the user + /// signing into an identity-provider based on observing the IdP-SignIn-Status + /// HTTP header. + /// + FederatedIdentityIdentityProviderSigninStatus, + + /// + /// Website setting which is used for UnusedSitePermissionsService to + /// store revoked permissions of unused sites from unused site permissions + /// feature. + /// + RevokedUnusedSitePermissions, + + /// + /// Similar to STORAGE_ACCESS, but applicable at the page-level rather than + /// being specific to a frame. + /// + TopLevelStorageAccess, + + /// + /// Setting to indicate whether user has opted in to allowing auto re-authn + /// via the FedCM API. + /// + FederatedIdentityAutoReauthnPermission, + + /// + /// Website setting which stores whether the user has explicitly registered + /// a website as an identity-provider. + /// + FederatedIdentityIdentityProviderRegistration, + + /// + /// Content setting which is used to indicate whether anti-abuse functionality + /// should be enabled. + /// + AntiAbuse, + + /// + /// Content setting used to indicate whether third-party storage partitioning + /// should be enabled. + /// + ThirdPartyStoragePartitioning, + + /// + /// Used to indicate whether HTTPS-First Mode is enabled on the hostname. + /// + HttpsEnforced, + + /// + /// Setting for enabling the `getAllScreensMedia` API. Spec link: + /// https://github.com/screen-share/capture-all-screens + /// + AllScreenCapture, + + /// + /// Stores per origin metadata for cookie controls. + /// + CookieControlsMetadata, + + /// + /// Content Setting for temporary 3PC accesses granted by user behavior + /// heuristics. + /// + TpcdHeuristicsGrants, + + /// + /// Content Setting for 3PC accesses granted by metadata delivered via the + /// component updater service. This type will only be used when + /// `net::features::kTpcdMetadataGrants` is enabled. + /// + TpcdMetadataGrants, + + /// + /// Content Setting for 3PC accesses granted via 3PC deprecation trial. + /// + TpcdTrial, + + /// + /// Content Setting for 3PC accesses granted via top-level 3PC deprecation + /// trial. Similar to TPCD_TRIAL, but applicable at the page-level for the + /// lifetime of the page that served the token, rather than being specific to + /// a requesting-origin/top-level-site combination and persistent. + /// + TopLevelTpcdTrial, + + /// + /// Content Setting for a first-party origin trial that allows websites to + /// enable third-party cookie deprecation. + /// ALLOW (default): no effect (e.g. third-party cookies allowed, if not + /// blocked otherwise). + /// BLOCK: third-party cookies blocked, but 3PCD mitigations enabled. + /// + TopLevelTpcdOriginTrial, + + /// + /// Content setting used to indicate whether entering picture-in-picture + /// automatically should be enabled. + /// + AutoPictureInPicture, + + /// + /// Whether user has opted into keeping file/directory permissions persistent + /// between visits for a given origin. When enabled, permission metadata + /// stored under |FILE_SYSTEM_ACCESS_CHOOSER_DATA| can auto-grant incoming + /// permission request. + /// + FileSystemAccessExtendedPermission, + + /// + /// Whether the FSA Persistent Permissions restore prompt is eligible to be + /// shown to the user, for a given origin. + /// + FileSystemAccessRestorePermission, + + /// + /// Whether an application capturing another tab, may scroll and zoom + /// the captured tab. + /// + CapturedSurfaceControl, + + /// + /// Content setting for access to smart card readers. + /// The "guard" content setting stores whether to allow sites to access the + /// Smart Card API. + /// + SmartCardGuard, + SmartCardData, + + /// + /// Content settings for access to printers for the Web Printing API. + /// + WebPrinting, + + /// + /// Content setting used to indicate whether entering HTML Fullscreen + /// automatically (i.e. without transient activation) should be enabled. + /// + AutomaticFullscreen, + + /// + /// Content settings used to indicate that a web app is allowed to prompt the + /// user for the installation of sub apps. + /// + SubAppInstallationPrompts, + + /// + /// Whether an application can enumerate audio output device. + /// + SpeakerSelection, + + /// + /// Content settings for access to the Direct Sockets API. + /// + DirectSockets, + + /// + /// Keyboard Lock API allows a site to capture keyboard inputs that would + /// otherwise be handled by the OS or the browser. + /// + KeyboardLock, + + /// + /// Pointer Lock API allows a site to hide the cursor and have exclusive + /// access to mouse inputs. + /// + PointerLock, + + /// + /// Website setting which is used for UnusedSitePermissionsService to store + /// auto-revoked notification permissions from abusive sites. + /// + RevokedAbusiveNotificationPermissions, + + /// + /// Content setting that controls tracking protection status per site. + /// BLOCK: Protections enabled. This is the default state. + /// ALLOW: Protections disabled. + /// + TrackingProtection, + + /// + /// With this permission, when the application calls `getDisplayMedia()`, a + /// system audio track can be returned without showing the display media + /// selection picker. The application can explicitly specify + /// `systemAudio: 'exclude'` or `video: true` to still show the display media + /// selection picker if needed. Please note that the setting only works for + /// WebUI. + /// + DisplayMediaSystemAudio, + + /// + /// Whether to use the higher-tier v8 optimizers for running JavaScript on the + /// page. + /// + JavascriptOptimizer, + + /// + /// Content Setting for the Storage Access Headers persistent origin trial + /// that allows origins to opt into the storage access header behavior. Should + /// be scoped to `REQUESTING_ORIGIN_AND_TOP_SCHEMEFUL_SITE_SCOPE` in order to + /// correspond to the design of persistent origin trials. See also: + /// https://github.com/cfredric/storage-access-headers + /// ALLOW: storage access request headers will be attached to cross-site + /// requests, and url requests will look for response headers from + /// origins to retry a request or load with storage access. + /// BLOCK (default): no effect. + /// + StorageAccessHeaderOriginTrial, + + /// + /// Whether or not sites can request Hand Tracking data within WebXR Sessions. + /// + HandTracking, + + /// + /// Website setting to indicate whether user has opted in to allow web apps to + /// install other web apps. + /// + WebAppInstallation, + + /// + /// Content settings for private network access in the context of the + /// Direct Sockets API. + /// + DirectSocketsPrivateNetworkAccess + } +} diff --git a/CefSharp/Enums/ContentSettingValues.cs b/CefSharp/Enums/ContentSettingValues.cs new file mode 100644 index 0000000000..6f609b3e8f --- /dev/null +++ b/CefSharp/Enums/ContentSettingValues.cs @@ -0,0 +1,21 @@ +// Copyright © 2024 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +namespace CefSharp.Enums +{ + /// + /// Supported content setting values. Should be kept in sync with Chromium's + /// ContentSetting type. + /// + public enum ContentSettingValues + { + Default = 0, + Allow, + Block, + Ask, + SessionOnly, + DetectImportantContent, + NumValues + } +} diff --git a/CefSharp/Enums/PermissionRequestType.cs b/CefSharp/Enums/PermissionRequestType.cs index 3d69e01fee..e0e9f94cde 100644 --- a/CefSharp/Enums/PermissionRequestType.cs +++ b/CefSharp/Enums/PermissionRequestType.cs @@ -19,21 +19,25 @@ public enum PermissionRequestType : uint ArSession = 1 << 1, CameraPanTiltZoom = 1 << 2, CameraStream = 1 << 3, - Clipboard = 1 << 4, - DiskQuota = 1 << 5, - LocalFonts = 1 << 6, - Geolocation = 1 << 7, - IdleDetection = 1 << 8, - MicStream = 1 << 9, - MidiSysex = 1 << 10, - MultipleDownloads = 1 << 11, - Notifications = 1 << 12, - ProtectedMediaIdentifier = 1 << 13, - RegisterProtocolHandler = 1 << 14, - SecurityAttestation = 1 << 15, - StorageAccess = 1 << 16, - U2FApiRequest = 1 << 17, - VrSession = 1 << 18, - WindowPlacement = 1 << 19 + CapturedSurfaceControl = 1 << 4, + Clipboard = 1 << 5, + TopLevelStorageAccess = 1 << 6, + DiskQuota = 1 << 7, + LocalFonts = 1 << 8, + Geolocation = 1 << 9, + Identity_Provider = 1 << 10, + IdleDetection = 1 << 11, + MicStream = 1 << 12, + MidiSysex = 1 << 13, + MultipleDownloads = 1 << 14, + Notifications = 1 << 15, + KeyboardLock = 1 << 16, + PointerLock = 1 << 17, + ProtectedMediaIdentifier = 1 << 18, + RegisterProtocolHandler = 1 << 19, + StorageAccess = 1 << 20, + VrSession = 1 << 21, + WindowManagement = 1 << 22, + FileSystemAccess = 1 << 23 } } diff --git a/CefSharp/Enums/ResultCode.cs b/CefSharp/Enums/ResultCode.cs new file mode 100644 index 0000000000..776b9eb741 --- /dev/null +++ b/CefSharp/Enums/ResultCode.cs @@ -0,0 +1,139 @@ +namespace CefSharp.Enums +{ + /// + /// CEF Exit Codes + /// + public enum ResultCode + { + // The following values should be kept in sync with Chromium's + // content::ResultCode type. + + NormalExit, + + /// + /// Process was killed by user or system. + /// + Killed, + + /// + /// Process hung. + /// + Hung, + + /// + /// A bad message caused the process termination. + /// + KilledBadMessage, + + /// + /// The GPU process exited because initialization failed. + /// + GpuDeadOnArrival, + + // The following values should be kept in sync with Chromium's + // chrome::ResultCode type. Unused chrome values are excluded. + + ChromeFirst, + + /// + /// A critical chrome file is missing. + /// + MissingData = 7, + + /// + /// Command line parameter is not supported. + /// + UnsupportedParam = 13, + + /// + /// The profile was in use on another host. + /// + ProfileInUse = 21, + + /// + /// Failed to pack an extension via the command line. + /// + PackExtensionError = 22, + + /// + /// The browser process exited early by passing the command line to another + /// running browser. + /// + NormalExitProcessNotified = 24, + + /// + /// A browser process was sandboxed. This should never happen. + /// + InvalidSandboxState = 31, + + /// + /// Cloud policy enrollment failed or was given up by user. + /// + CloudPolicyEnrollmentFailed = 32, + + /// + /// The GPU process was terminated due to context lost. + /// + GpuExitOnContextLost = 34, + + /// + /// An early startup command was executed and the browser must exit. + /// + NormalExitPackExtensionSuccess = 36, + + /// + /// The browser process exited because system resources are exhausted. The + /// system state can't be recovered and will be unstable. + /// + SystemResourceExhausted = 37, + + ChromeLast = 39, + + // The following values should be kept in sync with Chromium's + // sandbox::TerminationCodes type. + + SandboxFatalFirst = 7006, + + /// + /// Windows sandbox could not set the integrity level. + /// + SandboxFatalIntegrity = SandboxFatalFirst, + + /// + /// Windows sandbox could not lower the token. + /// + SandboxFatalDroptoken, + + /// + /// Windows sandbox failed to flush registry handles. + /// + SandboxFatalFlushandles, + + /// + /// Windows sandbox failed to forbid HCKU caching. + /// + SandboxFatalCachedisable, + + /// + /// Windows sandbox failed to close pending handles. + /// + SandboxFatalClosehandles, + + /// + /// Windows sandbox could not set the mitigation policy. + /// + SandboxFatalMitigation, + + /// + /// Windows sandbox exceeded the job memory limit. + /// + SandboxFatalMemoryExceeded, + + /// + /// Windows sandbox failed to warmup. + /// + SandboxFatalWarmup, + + SandboxFatalLast, + } +} diff --git a/CefSharp/Event/JavascriptBindingEventArgs.cs b/CefSharp/Event/JavascriptBindingEventArgs.cs index 955c735b8b..9275a1af2b 100644 --- a/CefSharp/Event/JavascriptBindingEventArgs.cs +++ b/CefSharp/Event/JavascriptBindingEventArgs.cs @@ -21,15 +21,22 @@ public class JavascriptBindingEventArgs : EventArgs /// public string ObjectName { get; private set; } + /// + /// URL of frame the call originated from. + /// + public string Url { get; private set; } + /// /// Constructor /// /// object repository + /// URL of frame the call originated from. /// object name - public JavascriptBindingEventArgs(IJavascriptObjectRepository objectRepository, string name) + public JavascriptBindingEventArgs(IJavascriptObjectRepository objectRepository, string url, string name) { ObjectRepository = objectRepository; ObjectName = name; + Url = url; } } } diff --git a/CefSharp/Event/JavascriptMessageReceivedEventArgs.cs b/CefSharp/Event/JavascriptMessageReceivedEventArgs.cs index 2c301a8c63..972ea93091 100644 --- a/CefSharp/Event/JavascriptMessageReceivedEventArgs.cs +++ b/CefSharp/Event/JavascriptMessageReceivedEventArgs.cs @@ -13,8 +13,7 @@ namespace CefSharp /// public class JavascriptMessageReceivedEventArgs : EventArgs { - private static readonly IBinder Binder = new DefaultBinder(); - + private static IBinder Binder = new DefaultBinder(); /// /// The frame that called CefSharp.PostMessage in Javascript @@ -60,5 +59,26 @@ public T ConvertMessageTo() } return (T)Binder.Bind(Message, typeof(T)); } + + /// + /// Provide a custom instance of + /// that will be used when + /// is called. You may wish to provide a custom instance in cases where you + /// wish to override the name conversion. + /// e.g. You wish to convert names from camelCase + /// + /// binder instance + /// + /// JavascriptMessageReceivedEventArgs.SetBinder(new DefaultBinder(new CamelCaseJavascriptNameConverter())); + /// + public static void SetBinder(IBinder binder) + { + if (binder == null) + { + throw new ArgumentNullException(nameof(binder)); + } + + Binder = binder; + } } } diff --git a/CefSharp/Handler/BrowserProcessHandler.cs b/CefSharp/Handler/BrowserProcessHandler.cs index ea2d8f1fe7..3605cfde30 100644 --- a/CefSharp/Handler/BrowserProcessHandler.cs +++ b/CefSharp/Handler/BrowserProcessHandler.cs @@ -3,6 +3,7 @@ // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. using System; +using System.Collections.Generic; namespace CefSharp.Handler { @@ -55,6 +56,43 @@ protected virtual void OnScheduleMessagePumpWork(long delay) } + /// + bool IBrowserProcessHandler.OnAlreadyRunningAppRelaunch(IReadOnlyDictionary commandLine, string currentDirectory) + { + return OnAlreadyRunningAppRelaunch(commandLine, currentDirectory); + } + + /// + /// Implement this method to provide app-specific behavior when an already + /// running app is relaunched with the same CefSettings.RootCachePath value. + /// For example, activate an existing app window or create a new app window. + /// + /// To avoid cache corruption only a single app instance is allowed to run for + /// a given CefSettings.RootCachePath value. On relaunch the app checks a + /// process singleton lock and then forwards the new launch arguments to the + /// already running app process before exiting early. Client apps should + /// therefore check the Cef.Initialize() return value for early exit before + /// proceeding. + /// + /// It's important to note that this method will be called on a CEF UI thread, + /// which by default is not the same as your application UI thread. + /// + /// + /// Command line arugments/switches + /// current directory (optional) + /// + /// Return true if the relaunch is handled or false for default relaunch + /// behavior. Default behavior will create a new default styled Chrome window. + /// + /// + /// The dictionary may contain keys that have empty string values + /// (arugments). + /// + protected virtual bool OnAlreadyRunningAppRelaunch(IReadOnlyDictionary commandLine, string currentDirectory) + { + return false; + } + /// /// IsDisposed /// diff --git a/CefSharp/Handler/DialogHandler.cs b/CefSharp/Handler/DialogHandler.cs index ab3e7807de..be90d3da6a 100644 --- a/CefSharp/Handler/DialogHandler.cs +++ b/CefSharp/Handler/DialogHandler.cs @@ -18,10 +18,12 @@ bool IDialogHandler.OnFileDialog( CefFileDialogMode mode, string title, string defaultFilePath, - List acceptFilters, + IReadOnlyCollection acceptFilters, + IReadOnlyCollection acceptExtensions, + IReadOnlyCollection acceptDescriptions, IFileDialogCallback callback) { - return OnFileDialog(chromiumWebBrowser, browser, mode, title, defaultFilePath, acceptFilters, callback); + return OnFileDialog(chromiumWebBrowser, browser, mode, title, defaultFilePath, acceptFilters, acceptExtensions, acceptDescriptions, callback); } /// @@ -46,6 +48,12 @@ bool IDialogHandler.OnFileDialog( /// (a) valid lower-cased MIME types (e.g. "text/*" or "image/*"), /// (b) individual file extensions (e.g. ".txt" or ".png"), /// (c) combined description and file extension delimited using "|" and ";" (e.g. "Image Types|.png;.gif;.jpg"). + /// provides the semicolon-delimited expansion of MIME + /// types to file extensions (if known, or empty string otherwise). + /// + /// Provides the descriptions for MIME types (if known, or empty string otherwise). + /// For example, the 'image/*' mime type might have extensions ".png;.jpg;.bmp;..." and description 'Image Files' + /// /// Callback interface for asynchronous continuation of file dialog requests. /// To display a custom dialog return true. To display the default dialog return false. protected virtual bool OnFileDialog( @@ -54,7 +62,9 @@ protected virtual bool OnFileDialog( CefFileDialogMode mode, string title, string defaultFilePath, - List acceptFilters, + IReadOnlyCollection acceptFilters, + IReadOnlyCollection acceptExtensions, + IReadOnlyCollection acceptDescriptions, IFileDialogCallback callback) { return false; diff --git a/CefSharp/Handler/DownloadHandler.cs b/CefSharp/Handler/DownloadHandler.cs index e7386aad07..2e714d93a4 100644 --- a/CefSharp/Handler/DownloadHandler.cs +++ b/CefSharp/Handler/DownloadHandler.cs @@ -31,16 +31,10 @@ protected virtual bool CanDownload(IWebBrowser chromiumWebBrowser, IBrowser brow return true; } - /// - /// Called before a download begins. - /// - /// the ChromiumWebBrowser control - /// The browser instance - /// Represents the file being downloaded. - /// Callback interface used to asynchronously continue a download. - void IDownloadHandler.OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) + /// + bool IDownloadHandler.OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) { - OnBeforeDownload(chromiumWebBrowser, browser, downloadItem, callback); + return OnBeforeDownload(chromiumWebBrowser, browser, downloadItem, callback); } /// @@ -50,9 +44,13 @@ void IDownloadHandler.OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser /// The browser instance /// Represents the file being downloaded. /// Callback interface used to asynchronously continue a download. - protected virtual void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) + /// Return true and execute either + /// asynchronously or in this method to continue or cancel the download. + /// Return false to proceed with default handling (cancel with Alloy style, + /// download shelf with Chrome style). + protected virtual bool OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) { - + return false; } /// diff --git a/CefSharp/Handler/ExtensionHandler .cs b/CefSharp/Handler/ExtensionHandler .cs deleted file mode 100644 index 04f62a4ddf..0000000000 --- a/CefSharp/Handler/ExtensionHandler .cs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright © 2021 The CefSharp Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. - -using System; - -namespace CefSharp.Handler -{ - /// - /// Implement this interface to handle events related to browser extensions. - /// The methods of this class will be called on the CEF UI thread. - /// See for information about extension loading. - /// - public class ExtensionHandler : IExtensionHandler - { - private bool isDisposed; - - /// - /// Called if the request fails. - /// - /// error code - void IExtensionHandler.OnExtensionLoadFailed(CefErrorCode errorCode) - { - OnExtensionLoadFailed(errorCode); - } - - /// - /// Called if the request fails. - /// - /// error code - protected virtual void OnExtensionLoadFailed(CefErrorCode errorCode) - { - - } - - /// - /// Called if the request succeeds. - /// - /// is the loaded extension. - void IExtensionHandler.OnExtensionLoaded(IExtension extension) - { - OnExtensionLoaded(extension); - } - - /// - /// Called if the request succeeds. - /// - /// is the loaded extension. - protected virtual void OnExtensionLoaded(IExtension extension) - { - - } - - /// - /// Called after the IExtension.Unload request has completed. - /// - /// is the unloaded extension - void IExtensionHandler.OnExtensionUnloaded(IExtension extension) - { - OnExtensionUnloaded(extension); - } - - /// - /// Called after the IExtension.Unload request has completed. - /// - /// is the unloaded extension - protected virtual void OnExtensionUnloaded(IExtension extension) - { - - } - - /// - /// Called when an extension needs a browser to host a background script specified via the "background" manifest key. - /// The browser will have no visible window and cannot be displayed. To allow creation of the browser optionally - /// modify newBrowser and settings and return false. To cancel creation of the browser - /// (and consequently cancel load of the background script) return true. Successful creation will be indicated by a call to - /// ILifeSpanHandler.OnAfterCreated, and IBrowserHost.IsBackgroundHost - /// will return true for the resulting browser. See https://developer.chrome.com/extensions/event_pages for more information - /// about extension background script usage. - /// - /// is the extension that is loading the background script - /// is an internally generated reference to an HTML page that will be used to - /// load the background script via a script src attribute - /// browser settings - /// To cancel creation of the browser (and consequently cancel load of the background script) return true, otherwise return false. - bool IExtensionHandler.OnBeforeBackgroundBrowser(IExtension extension, string url, IBrowserSettings settings) - { - return OnBeforeBackgroundBrowser(extension, url, settings); - } - - /// - /// Called when an extension needs a browser to host a background script specified via the "background" manifest key. - /// The browser will have no visible window and cannot be displayed. To allow creation of the browser optionally - /// modify newBrowser and settings and return false. To cancel creation of the browser - /// (and consequently cancel load of the background script) return true. Successful creation will be indicated by a call to - /// ILifeSpanHandler.OnAfterCreated, and IBrowserHost.IsBackgroundHost - /// will return true for the resulting browser. See https://developer.chrome.com/extensions/event_pages for more information - /// about extension background script usage. - /// - /// is the extension that is loading the background script - /// is an internally generated reference to an HTML page that will be used to - /// load the background script via a script src attribute - /// browser settings - /// To cancel creation of the browser (and consequently cancel load of the background script) return true, otherwise return false. - protected virtual bool OnBeforeBackgroundBrowser(IExtension extension, string url, IBrowserSettings settings) - { - return false; - } - - /// - /// Called when an extension API (e.g. chrome.tabs.create) requests creation of a new browser. - /// Successful creation will be indicated by a call to . - /// - /// the source of the API call - /// the source of the API call - /// may optionally be specified via the windowId property or - /// returned via the GetActiveBrowser() callback and provides the default for the new browser - /// is the position value optionally specified via the index property - /// is the URL that will be loaded in the browser - /// is true if the new browser should be active when opened - /// optionally modify if you are going to allow creation of the browser - /// optionally modify browser settings - /// To cancel creation of the browser return true. To allow creation return false and optionally modify windowInfo and settings - bool IExtensionHandler.OnBeforeBrowser(IExtension extension, IBrowser browser, IBrowser activeBrowser, int index, string url, bool active, IWindowInfo windowInfo, IBrowserSettings settings) - { - return OnBeforeBrowser(extension, browser, activeBrowser, index, url, active, windowInfo, settings); - } - - /// - /// Called when an extension API (e.g. chrome.tabs.create) requests creation of a new browser. - /// Successful creation will be indicated by a call to . - /// - /// the source of the API call - /// the source of the API call - /// may optionally be specified via the windowId property or - /// returned via the GetActiveBrowser() callback and provides the default for the new browser - /// is the position value optionally specified via the index property - /// is the URL that will be loaded in the browser - /// is true if the new browser should be active when opened - /// optionally modify if you are going to allow creation of the browser - /// optionally modify browser settings - /// To cancel creation of the browser return true. To allow creation return false and optionally modify windowInfo and settings - protected virtual bool OnBeforeBrowser(IExtension extension, IBrowser browser, IBrowser activeBrowser, int index, string url, bool active, IWindowInfo windowInfo, IBrowserSettings settings) - { - return false; - } - - /// - /// Called when no tabId is specified to an extension API call that accepts a tabId parameter (e.g. chrome.tabs.*). - /// - /// extension the call originates from - /// browser the call originates from - /// Incognito browsers should not be considered unless the source extension has incognito - /// access enabled, inwhich case this will be true - /// Return the browser that will be acted on by the API call or return null to act on . - /// The returned browser must share the same IRequestContext as - IBrowser IExtensionHandler.GetActiveBrowser(IExtension extension, IBrowser browser, bool includeIncognito) - { - return GetActiveBrowser(extension, browser, includeIncognito); - } - - /// - /// Called when no tabId is specified to an extension API call that accepts a tabId parameter (e.g. chrome.tabs.*). - /// - /// extension the call originates from - /// browser the call originates from - /// Incognito browsers should not be considered unless the source extension has incognito - /// access enabled, inwhich case this will be true - /// Return the browser that will be acted on by the API call or return null to act on . - /// The returned browser must share the same IRequestContext as - protected virtual IBrowser GetActiveBrowser(IExtension extension, IBrowser browser, bool includeIncognito) - { - return null; - } - - /// - /// Called when the tabId associated with is specified to an extension API call that accepts a tabId - /// parameter (e.g. chrome.tabs.*). - /// - /// extension the call originates from - /// browser the call originates from - /// Access to incognito browsers should not be allowed unless the source extension has - /// incognito access - /// enabled, in which case this will be true. - /// - /// Return true to allow access of false to deny access. - bool IExtensionHandler.CanAccessBrowser(IExtension extension, IBrowser browser, bool includeIncognito, IBrowser targetBrowser) - { - return CanAccessBrowser(extension, browser, includeIncognito, targetBrowser); - } - - /// - /// Called when the tabId associated with is specified to an extension API call that accepts a tabId - /// parameter (e.g. chrome.tabs.*). - /// - /// extension the call originates from - /// browser the call originates from - /// Access to incognito browsers should not be allowed unless the source extension has - /// incognito access - /// enabled, in which case this will be true. - /// - /// Return true to allow access of false to deny access. - protected virtual bool CanAccessBrowser(IExtension extension, IBrowser browser, bool includeIncognito, IBrowser targetBrowser) - { - return false; - } - - /// - /// Called to retrieve an extension resource that would normally be loaded from disk - /// (e.g. if a file parameter is specified to chrome.tabs.executeScript). - /// Localization substitutions will not be applied to resources handled via this method. - /// - /// extension the call originates from - /// browser the call originates from - /// is the requested relative file path. - /// callback used to handle custom resource requests - /// To handle the resource request return true and execute either synchronously or asynchronously. - /// For the default behavior which reads the resource from the extension directory on disk return false - bool IExtensionHandler.GetExtensionResource(IExtension extension, IBrowser browser, string file, IGetExtensionResourceCallback callback) - { - return GetExtensionResource(extension, browser, file, callback); - } - - /// - /// Called to retrieve an extension resource that would normally be loaded from disk - /// (e.g. if a file parameter is specified to chrome.tabs.executeScript). - /// Localization substitutions will not be applied to resources handled via this method. - /// - /// extension the call originates from - /// browser the call originates from - /// is the requested relative file path. - /// callback used to handle custom resource requests - /// To handle the resource request return true and execute either synchronously or asynchronously. - /// For the default behavior which reads the resource from the extension directory on disk return false - protected virtual bool GetExtensionResource(IExtension extension, IBrowser browser, string file, IGetExtensionResourceCallback callback) - { - return false; - } - - /// - /// IsDisposed - /// - public bool IsDisposed - { - get { return isDisposed; } - } - - protected virtual void Dispose(bool disposing) - { - isDisposed = true; - } - - /// - void IDisposable.Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/CefSharp/Handler/FindHandler.cs b/CefSharp/Handler/FindHandler.cs index 9c540f7752..e8362e0453 100644 --- a/CefSharp/Handler/FindHandler.cs +++ b/CefSharp/Handler/FindHandler.cs @@ -12,7 +12,7 @@ namespace CefSharp.Handler /// public class FindHandler : IFindHandler { - /// + /// void IFindHandler.OnFindResult(IWebBrowser chromiumWebBrowser, IBrowser browser, int identifier, int count, Rect selectionRect, int activeMatchOrdinal, bool finalUpdate) { OnFindResult(chromiumWebBrowser, browser, identifier, count, selectionRect, activeMatchOrdinal, finalUpdate); diff --git a/CefSharp/Handler/FrameHandler.cs b/CefSharp/Handler/FrameHandler.cs index 1af6564777..06409be520 100644 --- a/CefSharp/Handler/FrameHandler.cs +++ b/CefSharp/Handler/FrameHandler.cs @@ -12,7 +12,7 @@ namespace CefSharp.Handler /// public class FrameHandler : IFrameHandler { - private Dictionary frames = new Dictionary(); + private Dictionary frames = new Dictionary(); /// void IFrameHandler.OnFrameAttached(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, bool reattached) @@ -119,7 +119,7 @@ protected virtual void OnMainFrameChanged(IWebBrowser chromiumWebBrowser, IBrows } - private IFrame GetFrameById(long frameId) + private IFrame GetFrameById(string frameId) { if(frames.TryGetValue(frameId, out var frame)) { diff --git a/CefSharp/Handler/IBrowserProcessHandler.cs b/CefSharp/Handler/IBrowserProcessHandler.cs index d5b9688993..7d5a635584 100644 --- a/CefSharp/Handler/IBrowserProcessHandler.cs +++ b/CefSharp/Handler/IBrowserProcessHandler.cs @@ -3,6 +3,7 @@ // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. using System; +using System.Collections.Generic; namespace CefSharp { @@ -34,5 +35,33 @@ public interface IBrowserProcessHandler : IDisposable /// specified delay and any currently pending scheduled call should be /// cancelled. void OnScheduleMessagePumpWork(long delay); + + /// + /// Implement this method to provide app-specific behavior when an already + /// running app is relaunched with the same CefSettings.RootCachePath value. + /// For example, activate an existing app window or create a new app window. + /// + /// To avoid cache corruption only a single app instance is allowed to run for + /// a given CefSettings.RootCachePath value. On relaunch the app checks a + /// process singleton lock and then forwards the new launch arguments to the + /// already running app process before exiting early. Client apps should + /// therefore check the Cef.Initialize() return value for early exit before + /// proceeding. + /// + /// It's important to note that this method will be called on a CEF UI thread, + /// which by default is not the same as your application UI thread. + /// + /// + /// Readonly command line args + /// current directory (optional) + /// + /// Return true if the relaunch is handled or false for default relaunch + /// behavior. Default behavior will create a new default styled Chrome window. + /// + /// + /// The dictionary may contain keys that have empty string values + /// (arugments). + /// + bool OnAlreadyRunningAppRelaunch(IReadOnlyDictionary commandLine, string currentDirectory); } } diff --git a/CefSharp/Handler/IDialogHandler.cs b/CefSharp/Handler/IDialogHandler.cs index 7d2200ba15..0988ef04cd 100644 --- a/CefSharp/Handler/IDialogHandler.cs +++ b/CefSharp/Handler/IDialogHandler.cs @@ -33,6 +33,12 @@ public interface IDialogHandler /// (a) valid lower-cased MIME types (e.g. "text/*" or "image/*"), /// (b) individual file extensions (e.g. ".txt" or ".png"), /// (c) combined description and file extension delimited using "|" and ";" (e.g. "Image Types|.png;.gif;.jpg"). + /// provides the semicolon-delimited expansion of MIME + /// types to file extensions (if known, or empty string otherwise). + /// + /// Provides the descriptions for MIME types (if known, or empty string otherwise). + /// For example, the 'image/*' mime type might have extensions ".png;.jpg;.bmp;..." and description 'Image Files' + /// /// Callback interface for asynchronous continuation of file dialog requests. /// To display a custom dialog return true. To display the default dialog return false. bool OnFileDialog( @@ -41,7 +47,9 @@ bool OnFileDialog( CefFileDialogMode mode, string title, string defaultFilePath, - List acceptFilters, + IReadOnlyCollection acceptFilters, + IReadOnlyCollection acceptExtensions, + IReadOnlyCollection acceptDescriptions, IFileDialogCallback callback); } } diff --git a/CefSharp/Handler/IDownloadHandler.cs b/CefSharp/Handler/IDownloadHandler.cs index 3134645af4..fa6cf0e2e9 100644 --- a/CefSharp/Handler/IDownloadHandler.cs +++ b/CefSharp/Handler/IDownloadHandler.cs @@ -29,7 +29,11 @@ public interface IDownloadHandler /// The browser instance /// Represents the file being downloaded. /// Callback interface used to asynchronously continue a download. - void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback); + /// Return true and execute either + /// asynchronously or in this method to continue or cancel the download. + /// Return false to proceed with default handling (cancel with Alloy style, + /// download shelf with Chrome style). + bool OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback); /// /// Called when a download's status or progress information has been updated. This may be called multiple times before and after . diff --git a/CefSharp/Handler/IExtensionHandler.cs b/CefSharp/Handler/IExtensionHandler.cs deleted file mode 100644 index 4822b206c9..0000000000 --- a/CefSharp/Handler/IExtensionHandler.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright © 2018 The CefSharp Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. - -using System; - -namespace CefSharp -{ - /// - /// Implement this interface to handle events related to browser extensions. - /// The methods of this class will be called on the CEF UI thread. - /// See for information about extension loading. - /// - public interface IExtensionHandler : IDisposable - { - /// - /// Called if the request fails. - /// - /// error code - void OnExtensionLoadFailed(CefErrorCode errorCode); - - /// - /// Called if the request succeeds. - /// - /// is the loaded extension. - void OnExtensionLoaded(IExtension extension); - - /// - /// Called after the IExtension.Unload request has completed. - /// - /// is the unloaded extension - void OnExtensionUnloaded(IExtension extension); - - /// - /// Called when an extension needs a browser to host a background script specified via the "background" manifest key. - /// The browser will have no visible window and cannot be displayed. To allow creation of the browser optionally - /// modify newBrowser and settings and return false. To cancel creation of the browser - /// (and consequently cancel load of the background script) return true. Successful creation will be indicated by a call to - /// ILifeSpanHandler.OnAfterCreated, and IBrowserHost.IsBackgroundHost - /// will return true for the resulting browser. See https://developer.chrome.com/extensions/event_pages for more information - /// about extension background script usage. - /// - /// is the extension that is loading the background script - /// is an internally generated reference to an HTML page that will be used to - /// load the background script via a script src attribute - /// browser settings - /// To cancel creation of the browser (and consequently cancel load of the background script) return true, otherwise return false. - bool OnBeforeBackgroundBrowser(IExtension extension, string url, IBrowserSettings settings); - - /// - /// Called when an extension API (e.g. chrome.tabs.create) requests creation of a new browser. - /// Successful creation will be indicated by a call to . - /// - /// the source of the API call - /// the source of the API call - /// may optionally be specified via the windowId property or - /// returned via the GetActiveBrowser() callback and provides the default for the new browser - /// is the position value optionally specified via the index property - /// is the URL that will be loaded in the browser - /// is true if the new browser should be active when opened - /// optionally modify if you are going to allow creation of the browser - /// optionally modify browser settings - /// To cancel creation of the browser return true. To allow creation return false and optionally modify windowInfo and settings - bool OnBeforeBrowser(IExtension extension, IBrowser browser, IBrowser activeBrowser, int index, string url, bool active, IWindowInfo windowInfo, IBrowserSettings settings); - - /// - /// Called when no tabId is specified to an extension API call that accepts a tabId parameter (e.g. chrome.tabs.*). - /// - /// extension the call originates from - /// browser the call originates from - /// Incognito browsers should not be considered unless the source extension has incognito - /// access enabled, inwhich case this will be true - /// Return the browser that will be acted on by the API call or return null to act on . - /// The returned browser must share the same IRequestContext as - IBrowser GetActiveBrowser(IExtension extension, IBrowser browser, bool includeIncognito); - - /// - /// Called when the tabId associated with is specified to an extension API call that accepts a tabId - /// parameter (e.g. chrome.tabs.*). - /// - /// extension the call originates from - /// browser the call originates from - /// Access to incognito browsers should not be allowed unless the source extension has - /// incognito access - /// enabled, in which case this will be true. - /// - /// Return true to allow access of false to deny access. - bool CanAccessBrowser(IExtension extension, IBrowser browser, bool includeIncognito, IBrowser targetBrowser); - - /// - /// Called to retrieve an extension resource that would normally be loaded from disk - /// (e.g. if a file parameter is specified to chrome.tabs.executeScript). - /// Localization substitutions will not be applied to resources handled via this method. - /// - /// extension the call originates from - /// browser the call originates from - /// is the requested relative file path. - /// callback used to handle custom resource requests - /// To handle the resource request return true and execute either synchronously or asynchronously. - /// For the default behavior which reads the resource from the extension directory on disk return false - bool GetExtensionResource(IExtension extension, IBrowser browser, string file, IGetExtensionResourceCallback callback); - } -} diff --git a/CefSharp/Handler/IRequestHandler.cs b/CefSharp/Handler/IRequestHandler.cs index d9f2859fc0..f89f7d4985 100644 --- a/CefSharp/Handler/IRequestHandler.cs +++ b/CefSharp/Handler/IRequestHandler.cs @@ -135,6 +135,8 @@ bool OnOpenUrlFromTab(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame f /// The ChromiumWebBrowser control /// the browser object /// indicates how the process terminated. - void OnRenderProcessTerminated(IWebBrowser chromiumWebBrowser, IBrowser browser, CefTerminationStatus status); + /// error code + /// error message + void OnRenderProcessTerminated(IWebBrowser chromiumWebBrowser, IBrowser browser, CefTerminationStatus status, int errorCode, string errorMessage); } } diff --git a/CefSharp/Handler/RequestHandler.cs b/CefSharp/Handler/RequestHandler.cs index e93460579d..bf13837b82 100644 --- a/CefSharp/Handler/RequestHandler.cs +++ b/CefSharp/Handler/RequestHandler.cs @@ -205,9 +205,9 @@ protected virtual void OnRenderViewReady(IWebBrowser chromiumWebBrowser, IBrowse } /// - void IRequestHandler.OnRenderProcessTerminated(IWebBrowser chromiumWebBrowser, IBrowser browser, CefTerminationStatus status) + void IRequestHandler.OnRenderProcessTerminated(IWebBrowser chromiumWebBrowser, IBrowser browser, CefTerminationStatus status, int errorCode, string errorMessage) { - OnRenderProcessTerminated(chromiumWebBrowser, browser, status); + OnRenderProcessTerminated(chromiumWebBrowser, browser, status, errorCode, errorMessage); } /// @@ -216,7 +216,9 @@ void IRequestHandler.OnRenderProcessTerminated(IWebBrowser chromiumWebBrowser, I /// The ChromiumWebBrowser control. /// the browser object. /// indicates how the process terminated. - protected virtual void OnRenderProcessTerminated(IWebBrowser chromiumWebBrowser, IBrowser browser, CefTerminationStatus status) + /// error code + /// error message + protected virtual void OnRenderProcessTerminated(IWebBrowser chromiumWebBrowser, IBrowser browser, CefTerminationStatus status, int errorCode, string errorMessage) { } diff --git a/CefSharp/IBrowser.cs b/CefSharp/IBrowser.cs index 182167667f..ef636127a0 100644 --- a/CefSharp/IBrowser.cs +++ b/CefSharp/IBrowser.cs @@ -113,14 +113,14 @@ public interface IBrowser : IDisposable /// /// identifier /// frame or null - IFrame GetFrame(Int64 identifier); + IFrame GetFrameByIdentifier(string identifier); /// /// Returns the frame with the specified name, or NULL if not found. /// /// name of frame /// frame or null - IFrame GetFrame(string name); + IFrame GetFrameByName(string name); /// /// Returns the number of frames that currently exist. @@ -132,7 +132,7 @@ public interface IBrowser : IDisposable /// Returns the identifiers of all existing frames. /// /// list of frame identifiers - List GetFrameIdentifiers(); + List GetFrameIdentifiers(); /// /// Returns the names of all existing frames. @@ -145,12 +145,10 @@ public interface IBrowser : IDisposable /// bool IsDisposed { get; } - // - // Send a message to the specified |target_process|. Returns true if the - // message was sent successfully. - // - /*--cef()--*/ - //virtual bool SendProcessMessage(CefProcessId target_process, - // CefRefPtr message) =0; + /// + /// Gets a collection of all the current frames. + /// + /// frames + IReadOnlyCollection