├── .editorconfig
├── .gitattributes
├── .github
└── FUNDING.yml
├── .gitignore
├── CefSharp.Wpf.HwndHost.Example
├── App.config
├── App.xaml
├── App.xaml.cs
├── AssemblyInfo.cs
├── Behaviours
│ ├── HoverLinkBehaviour.cs
│ └── TextBoxBindingUpdateOnEnterBehaviour.cs
├── CefSharp.Wpf.HwndHost.Example.csproj
├── Converter
│ ├── EnvironmentConverter.cs
│ └── TitleConverter.cs
├── Handlers
│ └── DisplayHandler.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── TabbedMainWindow.xaml
├── TabbedMainWindow.xaml.cs
├── app.manifest
└── crash_reporter.cfg
├── CefSharp.Wpf.HwndHost.sln
├── CefSharp.Wpf.HwndHost
├── CefSettings.cs
├── CefSharp.Wpf.HwndHost.csproj
├── ChromiumWebBrowser.cs
├── FocusHandler.cs
├── Handler
│ └── IntegratedMessageLoopBrowserProcessHandler.cs
├── IWpfWebBrowser.cs
└── Internals
│ ├── DelegateCommand.cs
│ └── NoCloseLifespanHandler.cs
├── CefSharp.snk
├── LICENSE
├── NuGet.config
├── README.md
└── appveyor.yml
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | # Mostly based on https://github.com/dotnet/corefx/blob/master/.editorconfig
3 | # References
4 | # https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2017
5 | # https://kent-boogaart.com/blog/editorconfig-reference-for-c-developers
6 |
7 | # top-most EditorConfig file
8 | root = true
9 |
10 | # Default settings:
11 | # A newline ending every file
12 | # Use 4 spaces as indentation
13 | [*]
14 | insert_final_newline = true
15 | indent_style = space
16 | indent_size = 4
17 | charset = utf-8
18 | end_of_line = crlf
19 | trim_trailing_whitespace = true
20 |
21 | # Powershell files (build.ps1)
22 | [*.ps1]
23 | charset = utf-8-bom
24 |
25 | # Xml config files
26 | [*.{props,targets,config,nuspec,manifest}]
27 | indent_size = 2
28 |
29 | # Javascript Files
30 | [*.js]
31 | curly_bracket_next_line = true
32 | indent_brace_style = Allman
33 |
34 | # C++ Files
35 | [*.{cpp,h,in}]
36 | curly_bracket_next_line = true
37 | indent_brace_style = Allman
38 |
39 | [*.cs]
40 | # Capitalization styles
41 | dotnet_naming_style.constant_field_case_style.capitalization = pascal_case
42 | dotnet_naming_style.property_case_style.capitalization = pascal_case
43 | dotnet_naming_style.static_field_case_style.capitalization = pascal_case
44 | dotnet_naming_style.private_internal_field_case_style.capitalization = camel_case
45 |
46 | # New line preferences
47 | csharp_new_line_before_open_brace = all
48 | csharp_new_line_before_else = true
49 | csharp_new_line_before_catch = true
50 | csharp_new_line_before_finally = true
51 | csharp_new_line_before_members_in_object_initializers = true
52 | csharp_new_line_before_members_in_anonymous_types = true
53 | csharp_new_line_between_query_expression_clauses = true
54 |
55 | # Indentation preferences
56 | csharp_indent_block_contents = true
57 | csharp_indent_braces = false
58 | csharp_indent_case_contents = true
59 | csharp_indent_case_contents_when_block = false
60 | csharp_indent_switch_labels = true
61 | csharp_indent_labels = one_less_than_current
62 |
63 | # this.
64 | dotnet_style_qualification_for_field = false : suggestion
65 | dotnet_style_qualification_for_property = false : suggestion
66 | dotnet_style_qualification_for_method = false : suggestion
67 | dotnet_style_qualification_for_event = false : suggestion
68 |
69 | # Prefer using var
70 | csharp_style_var_for_built_in_types = true : none
71 | csharp_style_var_when_type_is_apparent = true : suggestion
72 | csharp_style_var_elsewhere = true : suggestion
73 |
74 | # use language keywords instead of BCL types
75 | dotnet_style_predefined_type_for_locals_parameters_members = true : suggestion
76 | dotnet_style_predefined_type_for_member_access = true : suggestion
77 |
78 | # Constant fields
79 | dotnet_naming_rule.constant_field_style.severity = error
80 | dotnet_naming_rule.constant_field_style.symbols = constant_field_target
81 | dotnet_naming_rule.constant_field_style.style = constant_field_case_style
82 |
83 | dotnet_naming_symbols.constant_field_target.applicable_kinds = field
84 | dotnet_naming_symbols.constant_field_target.required_modifiers = const
85 |
86 | # Properties
87 | dotnet_naming_rule.property_style.severity = error
88 | dotnet_naming_rule.property_style.symbols = property_target
89 | dotnet_naming_rule.property_style.style = property_case_style
90 |
91 | dotnet_naming_symbols.property_target.applicable_kinds = property
92 | dotnet_naming_symbols.property_target.required_modifiers = *
93 |
94 | # Static fields
95 | dotnet_naming_rule.static_field_style.severity = error
96 | dotnet_naming_rule.static_field_style.symbols = static_field_target
97 | dotnet_naming_rule.static_field_style.style = static_field_case_style
98 |
99 | dotnet_naming_symbols.static_field_target.applicable_kinds = field
100 | dotnet_naming_symbols.static_field_target.required_modifiers = static
101 |
102 | # Private and internal fields
103 | dotnet_naming_rule.private_internal_field_style.severity = error
104 | dotnet_naming_rule.private_internal_field_style.symbols = private_internal_field_target
105 | dotnet_naming_rule.private_internal_field_style.style = private_internal_field_case_style
106 |
107 | dotnet_naming_symbols.private_internal_field_target.applicable_kinds = field
108 | dotnet_naming_symbols.private_internal_field_target.applicable_accessibilities = private, internal
109 |
110 | # Code style defaults
111 | dotnet_sort_system_directives_first = true
112 | csharp_preserve_single_line_blocks = true
113 | csharp_prefer_braces = true
114 | csharp_preserve_single_line_statements = false
115 | dotnet_style_prefer_auto_properties = true : suggestion
116 |
117 | # Expression-level preferences
118 | dotnet_style_object_initializer = true : suggestion
119 | dotnet_style_collection_initializer = true : suggestion
120 | dotnet_style_explicit_tuple_names = false : suggestion
121 | dotnet_style_coalesce_expression = false : suggestion
122 | dotnet_style_null_propagation = false : suggestion
123 |
124 | # Expression-bodied members
125 | csharp_style_expression_bodied_methods = false : none
126 | csharp_style_expression_bodied_constructors = false : none
127 | csharp_style_expression_bodied_operators = false : none
128 | csharp_style_expression_bodied_properties = false : none
129 | csharp_style_expression_bodied_indexers = false : none
130 | csharp_style_expression_bodied_accessors = false : none
131 |
132 | # Space preferences
133 | csharp_space_after_cast = false
134 | csharp_space_after_colon_in_inheritance_clause = true
135 | csharp_space_after_comma = true
136 | csharp_space_after_dot = false
137 | csharp_space_after_keywords_in_control_flow_statements = true
138 | csharp_space_after_semicolon_in_for_statement = true
139 | csharp_space_around_binary_operators = before_and_after
140 | csharp_space_around_declaration_statements = do_not_ignore
141 | csharp_space_before_colon_in_inheritance_clause = true
142 | csharp_space_before_comma = false
143 | csharp_space_before_dot = false
144 | csharp_space_before_open_square_brackets = false
145 | csharp_space_before_semicolon_in_for_statement = false
146 | csharp_space_between_empty_square_brackets = false
147 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
148 | csharp_space_between_method_call_name_and_opening_parenthesis = false
149 | csharp_space_between_method_call_parameter_list_parentheses = false
150 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
151 | csharp_space_between_method_declaration_name_and_open_parenthesis = false
152 | csharp_space_between_method_declaration_parameter_list_parentheses = false
153 | csharp_space_between_parentheses = false
154 | csharp_space_between_square_brackets = false
155 |
156 | # Modifier order
157 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async : error
158 |
159 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | # github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | # patreon: # Replace with a single Patreon username
5 | # open_collective: # Replace with a single Open Collective username
6 | # ko_fi: # Replace with a single Ko-fi username
7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | # liberapay: # Replace with a single Liberapay username
10 | # issuehunt: # Replace with a single IssueHunt username
11 | # otechie: # Replace with a single Otechie username
12 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
15 | github: amaitland
16 | custom: https://paypal.me/AlexMaitland
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/App.xaml.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 The CefSharp Authors. All rights reserved.
2 | //
3 | // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4 |
5 | using System;
6 | using System.IO;
7 | using System.Windows;
8 | using CefSharp.Wpf.HwndHost.Handler;
9 |
10 | namespace CefSharp.Wpf.HwndHost.Example
11 | {
12 | ///
13 | /// Interaction logic for App.xaml
14 | ///
15 | public partial class App : Application
16 | {
17 | public App()
18 | {
19 | #if !NETCOREAPP3_1
20 | CefRuntime.SubscribeAnyCpuAssemblyResolver();
21 | #endif
22 | }
23 |
24 | protected override void OnStartup(StartupEventArgs e)
25 | {
26 | var settings = new CefSettings()
27 | {
28 | //By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
29 | CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache")
30 | };
31 |
32 | //Example of setting a command line argument
33 | //Enables WebRTC
34 | // - CEF Doesn't currently support permissions on a per browser basis see https://bitbucket.org/chromiumembedded/cef/issues/2582/allow-run-time-handling-of-media-access
35 | // - CEF Doesn't currently support displaying a UI for media access permissions
36 | //
37 | //NOTE: WebRTC Device Id's aren't persisted as they are in Chrome see https://bitbucket.org/chromiumembedded/cef/issues/2064/persist-webrtc-deviceids-across-restart
38 | settings.CefCommandLineArgs.Add("enable-media-stream");
39 | //https://peter.sh/experiments/chromium-command-line-switches/#use-fake-ui-for-media-stream
40 | settings.CefCommandLineArgs.Add("use-fake-ui-for-media-stream");
41 | //For screen sharing add (see https://bitbucket.org/chromiumembedded/cef/issues/2582/allow-run-time-handling-of-media-access#comment-58677180)
42 | settings.CefCommandLineArgs.Add("enable-usermedia-screen-capturing");
43 |
44 | //See https://github.com/cefsharp/CefSharp/wiki/General-Usage#multithreadedmessageloop
45 | //The default is true
46 | const bool multiThreadedMessageLoop = true;
47 |
48 | IBrowserProcessHandler browserProcessHandler = null;
49 |
50 | if(!multiThreadedMessageLoop)
51 | {
52 | settings.MultiThreadedMessageLoop = false;
53 | browserProcessHandler = new IntegratedMessageLoopBrowserProcessHandler(Dispatcher);
54 | }
55 |
56 | // Make sure you set performDependencyCheck false
57 | Cef.Initialize(settings, performDependencyCheck: false, browserProcessHandler: browserProcessHandler);
58 |
59 | base.OnStartup(e);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 The CefSharp Authors. All rights reserved.
2 | //
3 | // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4 |
5 | using System.Windows;
6 |
7 | [assembly: ThemeInfo(
8 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
9 | //(used if a resource is not found in the page,
10 | // or application resource dictionaries)
11 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
12 | //(used if a resource is not found in the page,
13 | // app, or any theme specific resource dictionaries)
14 | )]
15 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/Behaviours/HoverLinkBehaviour.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System;
3 | using Microsoft.Xaml.Behaviors;
4 |
5 | namespace CefSharp.Wpf.HwndHost.Example.Behaviours
6 | {
7 | public class HoverLinkBehaviour : Behavior
8 | {
9 | // Using a DependencyProperty as the backing store for HoverLink. This enables animation, styling, binding, etc...
10 | public static readonly DependencyProperty HoverLinkProperty = DependencyProperty.Register("HoverLink", typeof(string), typeof(HoverLinkBehaviour), new PropertyMetadata(string.Empty));
11 |
12 | public string HoverLink
13 | {
14 | get { return (string)GetValue(HoverLinkProperty); }
15 | set { SetValue(HoverLinkProperty, value); }
16 | }
17 |
18 | protected override void OnAttached()
19 | {
20 | AssociatedObject.StatusMessage += OnStatusMessageChanged;
21 | }
22 |
23 | protected override void OnDetaching()
24 | {
25 | AssociatedObject.StatusMessage -= OnStatusMessageChanged;
26 | }
27 |
28 | private void OnStatusMessageChanged(object sender, StatusMessageEventArgs e)
29 | {
30 | var chromiumWebBrowser = sender as ChromiumWebBrowser;
31 | chromiumWebBrowser.Dispatcher.BeginInvoke((Action)(() => HoverLink = e.Value));
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/Behaviours/TextBoxBindingUpdateOnEnterBehaviour.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 | using System.Windows.Input;
3 | using Microsoft.Xaml.Behaviors;
4 |
5 | namespace CefSharp.Wpf.HwndHost.Example.Behaviours
6 | {
7 | public class TextBoxBindingUpdateOnEnterBehaviour : Behavior
8 | {
9 | protected override void OnAttached()
10 | {
11 | AssociatedObject.KeyDown += OnTextBoxKeyDown;
12 | }
13 |
14 | protected override void OnDetaching()
15 | {
16 | AssociatedObject.KeyDown -= OnTextBoxKeyDown;
17 | }
18 |
19 | private void OnTextBoxKeyDown(object sender, KeyEventArgs e)
20 | {
21 | if (e.Key == Key.Enter)
22 | {
23 | var txtBox = sender as TextBox;
24 | txtBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/CefSharp.Wpf.HwndHost.Example.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | WinExe
4 | true
5 | netcoreapp3.1;net462
6 | CefSharp.Wpf.HwndHost.Example
7 | true
8 | CefSharp.Wpf.HwndHost.Example.App
9 | AnyCPU
10 | app.manifest
11 | 9.0
12 |
13 |
14 |
15 | win-x64
16 | false
17 |
18 |
19 |
20 |
21 | all
22 | runtime; build; native; contentfiles; analyzers; buildtransitive
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | PreserveNewest
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/Converter/EnvironmentConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 |
5 | namespace CefSharp.Wpf.HwndHost.Example.Converter
6 | {
7 | public class EnvironmentConverter : IValueConverter
8 | {
9 | object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
10 | {
11 | return Environment.Is64BitProcess ? "x64" : "x86";
12 | }
13 |
14 | object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
15 | {
16 | return Binding.DoNothing;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/Converter/TitleConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 |
5 | namespace CefSharp.Wpf.HwndHost.Example.Converter
6 | {
7 | public class TitleConverter : IValueConverter
8 | {
9 | object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
10 | {
11 | return "CefSharp.MinimalExample.Wpf.HwndHost - " + (value ?? "No Title Specified");
12 | }
13 |
14 | object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
15 | {
16 | return Binding.DoNothing;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/Handlers/DisplayHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls;
3 | using System.Windows.Media;
4 |
5 | namespace CefSharp.Wpf.HwndHost.Example.Handlers
6 | {
7 | public class DisplayHandler : CefSharp.Handler.DisplayHandler
8 | {
9 | private Border parent;
10 | private Window fullScreenWindow;
11 |
12 | protected override void OnFullscreenModeChange(IWebBrowser chromiumWebBrowser, IBrowser browser, bool fullscreen)
13 | {
14 | var webBrowser = (ChromiumWebBrowser)chromiumWebBrowser;
15 |
16 | _ = webBrowser.Dispatcher.InvokeAsync(() =>
17 | {
18 | if (fullscreen)
19 | {
20 | //In this example the parent is a Border, if your parent is a different type
21 | //of control then update this code accordingly.
22 | parent = (Border)VisualTreeHelper.GetParent(webBrowser);
23 |
24 | //NOTE: If the ChromiumWebBrowser instance doesn't have a direct reference to
25 | //the DataContext in this case the BrowserTabViewModel then your bindings won't
26 | //be updated/might cause issues like the browser reloads the Url when exiting
27 | //fullscreen.
28 | parent.Child = null;
29 |
30 | fullScreenWindow = new Window
31 | {
32 | WindowStyle = WindowStyle.None,
33 | WindowState = WindowState.Maximized,
34 | Content = webBrowser
35 | };
36 | fullScreenWindow.Loaded += (_,_) => webBrowser.Focus();
37 |
38 | fullScreenWindow.ShowDialog();
39 | }
40 | else
41 | {
42 | fullScreenWindow.Content = null;
43 |
44 | parent.Child = webBrowser;
45 |
46 | fullScreenWindow.Close();
47 | fullScreenWindow = null;
48 | parent = null;
49 | }
50 | });
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
50 |
51 |
52 |
53 |
54 |
55 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | Chromium: , CEF: , CefSharp: , Environment:
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using CefSharp.Wpf.HwndHost.Example.Handlers;
3 |
4 | namespace CefSharp.Wpf.HwndHost.Example
5 | {
6 | ///
7 | /// Interaction logic for MainWindow.xaml
8 | ///
9 | public partial class MainWindow : Window
10 | {
11 | public MainWindow()
12 | {
13 | InitializeComponent();
14 |
15 | Browser.DisplayHandler = new DisplayHandler();
16 | }
17 |
18 | private void ShowDevToolsClick(object sender, RoutedEventArgs e)
19 | {
20 | if (Browser.IsBrowserInitialized)
21 | {
22 | Browser.ShowDevTools();
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/TabbedMainWindow.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/TabbedMainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls;
3 |
4 | namespace CefSharp.Wpf.HwndHost.Example
5 | {
6 | ///
7 | /// Interaction logic for TabbedMainWindow.xaml
8 | ///
9 | public partial class TabbedMainWindow : Window
10 | {
11 | public TabbedMainWindow()
12 | {
13 | InitializeComponent();
14 |
15 | NewTab();
16 | NewTab();
17 | NewTab();
18 | }
19 |
20 | private void NewTab()
21 | {
22 | var browser = new ChromiumWebBrowser("https://example.com/");
23 | browser.Address = "https://example.com/";
24 | Tabs.Items.Add(new TabItem { Header = "Tab", Content = browser });
25 | }
26 |
27 | private void OnNewTabButtonClick(object sender, RoutedEventArgs e)
28 | {
29 | NewTab();
30 | }
31 |
32 | private void OnCloseTabButtonClick(object sender, RoutedEventArgs e)
33 | {
34 | var Tab = (TabItem)Tabs.Items[Tabs.SelectedIndex];
35 | var browser = (ChromiumWebBrowser)Tab.Content;
36 | Tab.Content = null;
37 | browser.Dispose();
38 | Tabs.Items.Remove(Tab);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
49 |
50 |
51 |
52 |
55 | PerMonitorV2
56 | true/PM
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.Example/crash_reporter.cfg:
--------------------------------------------------------------------------------
1 | # Crash reporting is configured using an INI-style config file named
2 | # "crash_reporter.cfg".
3 | # This file **must** be placed next to the main application executable.
4 | # Comments start with a hash character and must be on their own line.
5 | # See https://bitbucket.org/chromiumembedded/cef/wiki/CrashReporting.md
6 | # for further details
7 |
8 | [Config]
9 | ProductName=CefSharp.MinimalExample.Wpf.HwndHost
10 | ProductVersion=1.0.0
11 | AppName=CefSharp.MinimalExample
12 | ExternalHandler=CefSharp.BrowserSubprocess.exe
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30204.135
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.Wpf.HwndHost", "CefSharp.Wpf.HwndHost\CefSharp.Wpf.HwndHost.csproj", "{B0942DE1-D92A-4EB4-A407-47D2E223108B}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.Wpf.HwndHost.Example", "CefSharp.Wpf.HwndHost.Example\CefSharp.Wpf.HwndHost.Example.csproj", "{9F7C8D25-3BD2-414B-9C6C-52E371E0C5E8}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{59D2B203-3AD7-49F7-A64F-868440F29D5C}"
11 | ProjectSection(SolutionItems) = preProject
12 | appveyor.yml = appveyor.yml
13 | NuGet.config = NuGet.config
14 | EndProjectSection
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Release|Any CPU = Release|Any CPU
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {B0942DE1-D92A-4EB4-A407-47D2E223108B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {B0942DE1-D92A-4EB4-A407-47D2E223108B}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {B0942DE1-D92A-4EB4-A407-47D2E223108B}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {B0942DE1-D92A-4EB4-A407-47D2E223108B}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {9F7C8D25-3BD2-414B-9C6C-52E371E0C5E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {9F7C8D25-3BD2-414B-9C6C-52E371E0C5E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {9F7C8D25-3BD2-414B-9C6C-52E371E0C5E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {9F7C8D25-3BD2-414B-9C6C-52E371E0C5E8}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {B97382D3-C9AF-48BB-88B2-2094A9B20BAD}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost/CefSettings.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 The CefSharp Authors. All rights reserved.
2 | //
3 | // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4 |
5 | namespace CefSharp.Wpf.HwndHost
6 | {
7 | ///
8 | /// Initialization settings. Many of these and other settings can also configured using command-line switches.
9 | ///
10 | public class CefSettings : CefSettingsBase
11 | {
12 | ///
13 | /// Intialize with default values
14 | ///
15 | public CefSettings() : base()
16 | {
17 | // CEF doesn't call GetAuthCredentials unless the Chrome login prompt is disabled
18 | // https://github.com/chromiumembedded/cef/issues/3603
19 | CefCommandLineArgs.Add("disable-chrome-login-prompt");
20 |
21 | // Disable "Restore pages" popup after incorrect shutdown
22 | // https://github.com/chromiumembedded/cef/issues/3767
23 | CefCommandLineArgs.Add("hide-crash-restore-bubble");
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost/CefSharp.Wpf.HwndHost.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp3.1;net462
4 | true
5 | CefSharp.Wpf.HwndHost
6 | true
7 | true
8 | 131.3.50
9 | The CefSharp Authors
10 | The CefSharp Chromium-based browser component. The CefSharp.Wpf.HwndHost.ChromiumWebBrowser control is a drop in replacement for the CefSharp.Wpf.ChromiumWebBrowser that's rougly equivilent to hosting the WinForms version in WPF.
11 | Copyright © The CefSharp Authors
12 | true
13 | https://github.com/cefsharp/CefSharp.Wpf.HwndHost/
14 | LICENSE
15 | bin\$(Configuration)\$(TargetFramework)\CefSharp.Wpf.HwndHost.xml
16 | embedded
17 | true
18 | true
19 |
20 | NoAction
21 | true
22 | ..\CefSharp.snk
23 | 9.0
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | all
37 | runtime; build; native; contentfiles; analyzers; buildtransitive
38 |
39 |
40 | all
41 | runtime; build; native; contentfiles; analyzers; buildtransitive
42 |
43 |
44 |
45 |
46 |
47 | True
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost/ChromiumWebBrowser.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 The CefSharp Authors. All rights reserved.
2 | //
3 | // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4 |
5 | using System;
6 | using System.Runtime.CompilerServices;
7 | using System.Runtime.InteropServices;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 | using System.Windows;
11 | using System.Windows.Input;
12 | using System.Windows.Interop;
13 | using System.Windows.Threading;
14 | using CefSharp.DevTools.Page;
15 | using CefSharp.Internals;
16 | using CefSharp.Structs;
17 | using CefSharp.Wpf.HwndHost.Internals;
18 |
19 | namespace CefSharp.Wpf.HwndHost
20 | {
21 | ///
22 | /// ChromiumWebBrowser is the WPF web browser control
23 | ///
24 | ///
25 | ///
26 | /// based on https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/walkthrough-hosting-a-win32-control-in-wpf
27 | /// and https://stackoverflow.com/questions/6500336/custom-dwm-drawn-window-frame-flickers-on-resizing-if-the-window-contains-a-hwnd/17471534#17471534
28 | public class ChromiumWebBrowser : System.Windows.Interop.HwndHost, IWebBrowserInternal, IWpfWebBrowser
29 | {
30 | public const string BrowserNotInitializedExceptionErrorMessage =
31 | "The ChromiumWebBrowser instance creates the underlying Chromium Embedded Framework (CEF) browser instance in an async fashion. " +
32 | "The undelying CefBrowser instance is not yet initialized. Use the IsBrowserInitializedChanged event and check " +
33 | "the IsBrowserInitialized property to determine when the browser has been initialized.";
34 | private const string CefInitializeFailedErrorMessage = "Cef.Initialize() failed.Check the log file see https://github.com/cefsharp/CefSharp/wiki/Trouble-Shooting#log-file for details.";
35 | 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.";
36 |
37 | [DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
38 | private static extern IntPtr CreateWindowEx(int dwExStyle,
39 | string lpszClassName,
40 | string lpszWindowName,
41 | int style,
42 | int x, int y,
43 | int width, int height,
44 | IntPtr hwndParent,
45 | IntPtr hMenu,
46 | IntPtr hInst,
47 | [MarshalAs(UnmanagedType.AsAny)] object pvParam);
48 |
49 | [DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
50 | private static extern bool DestroyWindow(IntPtr hwnd);
51 |
52 | [DllImport("user32.dll", EntryPoint = "GetWindowLong")]
53 | private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int index);
54 |
55 | [DllImport("user32.dll", EntryPoint = "SetWindowLong")]
56 | private static extern int SetWindowLong32(HandleRef hWnd, int index, int dwNewLong);
57 |
58 | [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
59 | private static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int index, IntPtr dwNewLong);
60 |
61 | // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongptra
62 | //SetWindowLongPtr for x64, SetWindowLong for x86
63 | private void RemoveExNoActivateStyle(IntPtr hwnd)
64 | {
65 | var exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
66 |
67 | if (IntPtr.Size == 8)
68 | {
69 | if ((exStyle.ToInt64() & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE)
70 | {
71 | exStyle = new IntPtr(exStyle.ToInt64() & ~WS_EX_NOACTIVATE);
72 | //Remove WS_EX_NOACTIVATE
73 | SetWindowLongPtr64(new HandleRef(this, hwnd), GWL_EXSTYLE, exStyle);
74 | }
75 | }
76 | else
77 | {
78 | if ((exStyle.ToInt32() & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE)
79 | {
80 | //Remove WS_EX_NOACTIVATE
81 | SetWindowLong32(new HandleRef(this, hwnd), GWL_EXSTYLE, (int)(exStyle.ToInt32() & ~WS_EX_NOACTIVATE));
82 | }
83 | }
84 | }
85 |
86 | private const int WS_CHILD = 0x40000000,
87 | WS_VISIBLE = 0x10000000,
88 | LBS_NOTIFY = 0x00000001,
89 | HOST_ID = 0x00000002,
90 | LISTBOX_ID = 0x00000001,
91 | WS_VSCROLL = 0x00200000,
92 | WS_BORDER = 0x00800000,
93 | WS_CLIPCHILDREN = 0x02000000;
94 |
95 | private const uint WS_EX_NOACTIVATE = 0x08000000;
96 | private const int GWL_EXSTYLE = -20;
97 |
98 | ///
99 | /// Handle we'll use to host the browser
100 | ///
101 | private IntPtr hwndHost;
102 | ///
103 | /// The managed cef browser adapter
104 | ///
105 | private IBrowserAdapter managedCefBrowserAdapter;
106 | ///
107 | /// The ignore URI change
108 | ///
109 | private bool ignoreUriChange;
110 | ///
111 | /// Initial address
112 | ///
113 | private string initialAddress;
114 | ///
115 | /// Used to stop multiple threads trying to load the initial Url multiple times.
116 | /// If the Address property is bound after the browser is initialized
117 | ///
118 | private bool initialLoadCalled;
119 | ///
120 | /// Has the underlying Cef Browser been created (slightly different to initliazed in that
121 | /// the browser is initialized in an async fashion)
122 | ///
123 | private bool browserCreated;
124 | ///
125 | /// The browser initialized - boolean represented as 0 (false) and 1(true) as we use Interlocker to increment/reset
126 | ///
127 | private int browserInitialized;
128 | ///
129 | /// The browser
130 | ///
131 | private IBrowser browser;
132 | ///
133 | /// Browser initialization settings
134 | ///
135 | private IBrowserSettings browserSettings;
136 | ///
137 | /// The request context (we deliberately use a private variable so we can throw an exception if
138 | /// user attempts to set after browser created)
139 | ///
140 | private IRequestContext requestContext;
141 | ///
142 | /// A flag that indicates whether or not the designer is active
143 | /// NOTE: Needs to be static for OnApplicationExit
144 | ///
145 | private static bool DesignMode;
146 |
147 | ///
148 | /// The value for disposal, if it's 1 (one) then this instance is either disposed
149 | /// or in the process of getting disposed
150 | ///
151 | private int disposeSignaled;
152 |
153 | ///
154 | /// If true the the WS_EX_NOACTIVATE style will be removed so that future mouse clicks
155 | /// inside the browser correctly activate and focus the window.
156 | ///
157 | private bool removeExNoActivateStyle;
158 |
159 | ///
160 | /// Current DPI Scale
161 | ///
162 | private double dpiScale;
163 |
164 | ///
165 | /// The HwndSource RootVisual (Window) - We store a reference
166 | /// to unsubscribe event handlers
167 | ///
168 | private Window sourceWindow;
169 |
170 | ///
171 | /// Store the previous window state, used to determine if the
172 | /// Windows was previous
173 | /// and resume rendering
174 | ///
175 | private WindowState previousWindowState;
176 |
177 | ///
178 | /// This flag is set when the browser gets focus before the underlying CEF browser
179 | /// has been initialized.
180 | ///
181 | private bool initialFocus;
182 |
183 | ///
184 | /// Initial browser load task complection source
185 | ///
186 | private TaskCompletionSource initialLoadTaskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
187 |
188 | ///
189 | /// Initial browser load action
190 | ///
191 | private Action initialLoadAction;
192 |
193 | ///
194 | /// Activates browser upon creation, the default value is false. Prior to version 73
195 | /// the default behaviour was to activate browser on creation (Equivilent of setting this property to true).
196 | /// To restore this behaviour set this value to true immediately after you create the instance.
197 | /// https://bitbucket.org/chromiumembedded/cef/issues/1856/branch-2526-cef-activates-browser-window
198 | ///
199 | public bool ActivateBrowserOnCreation { get; set; }
200 |
201 | ///
202 | /// Gets a value indicating whether this instance is disposed.
203 | ///
204 | /// if this instance is disposed; otherwise, .
205 | public bool IsDisposed
206 | {
207 | get
208 | {
209 | return Interlocked.CompareExchange(ref disposeSignaled, 1, 1) == 1;
210 | }
211 | }
212 |
213 | ///
214 | /// Gets or sets the browser settings.
215 | ///
216 | /// The browser settings.
217 | public IBrowserSettings BrowserSettings
218 | {
219 | get { return browserSettings; }
220 | set
221 | {
222 | if (browserCreated)
223 | {
224 | throw new Exception("Browser has already been created. BrowserSettings must be " +
225 | "set before the underlying CEF browser is created.");
226 | }
227 |
228 | //New instance is created in the constructor, if you use
229 | //xaml to initialize browser settings then it will also create a new
230 | //instance, so we dispose of the old one
231 | if (browserSettings != null && browserSettings.AutoDispose)
232 | {
233 | browserSettings.Dispose();
234 | }
235 |
236 | browserSettings = value;
237 | }
238 | }
239 | ///
240 | /// Gets or sets the request context.
241 | ///
242 | /// The request context.
243 | public IRequestContext RequestContext
244 | {
245 | get { return requestContext; }
246 | set
247 | {
248 | if (browserCreated)
249 | {
250 | throw new Exception("Browser has already been created. RequestContext must be " +
251 | "set before the underlying CEF browser is created.");
252 | }
253 | if (value != null && !Core.ObjectFactory.RequestContextType.IsAssignableFrom(value.UnWrap().GetType()))
254 | {
255 | throw new Exception(string.Format("RequestContext can only be of type {0} or null", Core.ObjectFactory.RequestContextType));
256 | }
257 | requestContext = value;
258 | }
259 | }
260 | ///
261 | /// Implement and assign to handle dialog events.
262 | ///
263 | /// The dialog handler.
264 | public IDialogHandler DialogHandler { get; set; }
265 | ///
266 | /// Implement and assign to handle events related to JavaScript Dialogs.
267 | ///
268 | /// The js dialog handler.
269 | public IJsDialogHandler JsDialogHandler { get; set; }
270 | ///
271 | /// Implement and assign to handle events related to key press.
272 | ///
273 | /// The keyboard handler.
274 | public IKeyboardHandler KeyboardHandler { get; set; }
275 | ///
276 | /// Implement and assign to handle events related to browser requests.
277 | ///
278 | /// The request handler.
279 | public IRequestHandler RequestHandler { get; set; }
280 | ///
281 | /// Implement and assign to handle events related to downloading files.
282 | ///
283 | /// The download handler.
284 | public IDownloadHandler DownloadHandler { get; set; }
285 | ///
286 | /// Implement and assign to handle events related to browser load status.
287 | ///
288 | /// The load handler.
289 | public ILoadHandler LoadHandler { get; set; }
290 | ///
291 | /// Implement and assign to handle events related to popups.
292 | ///
293 | /// The life span handler.
294 | public ILifeSpanHandler LifeSpanHandler { get; set; }
295 | ///
296 | /// Implement and assign to handle events related to browser display state.
297 | ///
298 | /// The display handler.
299 | public IDisplayHandler DisplayHandler { get; set; }
300 | ///
301 | /// Implement and assign to handle events related to the browser context menu
302 | ///
303 | /// The menu handler.
304 | public IContextMenuHandler MenuHandler { get; set; }
305 | ///
306 | /// Implement and assign to handle events related to the browser component's focus
307 | ///
308 | /// The focus handler.
309 | public IFocusHandler FocusHandler { get; set; }
310 | ///
311 | /// Implement and assign to handle events related to dragging.
312 | ///
313 | /// The drag handler.
314 | public IDragHandler DragHandler { get; set; }
315 | ///
316 | /// Implement and control the loading of resources
317 | ///
318 | /// The resource handler factory.
319 | public IResourceRequestHandlerFactory ResourceRequestHandlerFactory { get; set; }
320 | ///
321 | /// Implement and assign to handle messages from the render process.
322 | ///
323 | /// The render process message handler.
324 | public IRenderProcessMessageHandler RenderProcessMessageHandler { get; set; }
325 | ///
326 | /// Implement to handle events related to find results.
327 | ///
328 | /// The find handler.
329 | public IFindHandler FindHandler { get; set; }
330 |
331 | ///
332 | /// Implement to handle audio events.
333 | ///
334 | public IAudioHandler AudioHandler { get; set; }
335 |
336 | ///
337 | /// Implement to handle frame events.
338 | ///
339 | public IFrameHandler FrameHandler { get; set; }
340 |
341 | ///
342 | /// Implement to handle events related to permission requests.
343 | ///
344 | public IPermissionHandler PermissionHandler { get; set; }
345 |
346 | ///
347 | /// Event handler for receiving Javascript console messages being sent from web pages.
348 | /// 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
349 | /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
350 | /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
351 | /// (The exception to this is when your running with settings.MultiThreadedMessageLoop = false, then they'll be the same thread).
352 | ///
353 | public event EventHandler ConsoleMessage;
354 |
355 | ///
356 | /// Event handler for changes to the status message.
357 | /// 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
358 | /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang.
359 | /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
360 | /// (The exception to this is when your running with settings.MultiThreadedMessageLoop = false, then they'll be the same thread).
361 | ///
362 | public event EventHandler StatusMessage;
363 |
364 | ///
365 | /// Event handler that will get called when the browser begins loading a frame. Multiple frames may be loading at the same
366 | /// time. Sub-frames may start or continue loading after the main frame load has ended. This method may not be called for a
367 | /// particular frame if the load request for that frame fails. For notification of overall browser load status use
368 | /// OnLoadingStateChange instead.
369 | /// 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
370 | /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
371 | /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
372 | ///
373 | /// Whilst this may seem like a logical place to execute js, it's called before the DOM has been loaded, implement
374 | /// as it's called when the underlying V8Context is created
375 | ///
376 | public event EventHandler FrameLoadStart;
377 |
378 | ///
379 | /// Event handler that will get called when the browser is done loading a frame. Multiple frames may be loading at the same
380 | /// time. Sub-frames may start or continue loading after the main frame load has ended. This method will always be called
381 | /// for all frames irrespective of whether the request completes successfully.
382 | /// 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
383 | /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
384 | /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
385 | ///
386 | public event EventHandler FrameLoadEnd;
387 |
388 | ///
389 | /// Event handler that will get called when the resource load for a navigation fails or is canceled.
390 | /// 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
391 | /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
392 | /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
393 | ///
394 | public event EventHandler LoadError;
395 |
396 | ///
397 | /// Event handler that will get called when the Loading state has changed.
398 | /// This event will be fired twice. Once when loading is initiated either programmatically or
399 | /// by user action, and once when loading is terminated due to completion, cancellation of failure.
400 | /// 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
401 | /// thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang..
402 | /// To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
403 | ///
404 | public event EventHandler LoadingStateChanged;
405 |
406 | ///
407 | /// Event handler that will get called when the message that originates from CefSharp.PostMessage
408 | ///
409 | public event EventHandler JavascriptMessageReceived;
410 |
411 | ///
412 | /// Navigates to the previous page in the browser history. Will automatically be enabled/disabled depending on the
413 | /// browser state.
414 | ///
415 | /// The back command.
416 | public ICommand BackCommand { get; private set; }
417 | ///
418 | /// Navigates to the next page in the browser history. Will automatically be enabled/disabled depending on the
419 | /// browser state.
420 | ///
421 | /// The forward command.
422 | public ICommand ForwardCommand { get; private set; }
423 | ///
424 | /// Reloads the content of the current page. Will automatically be enabled/disabled depending on the browser state.
425 | ///
426 | /// The reload command.
427 | public ICommand ReloadCommand { get; private set; }
428 | ///
429 | /// Prints the current browser contents.
430 | ///
431 | /// The print command.
432 | public ICommand PrintCommand { get; private set; }
433 | ///
434 | /// Increases the zoom level.
435 | ///
436 | /// The zoom in command.
437 | public ICommand ZoomInCommand { get; private set; }
438 | ///
439 | /// Decreases the zoom level.
440 | ///
441 | /// The zoom out command.
442 | public ICommand ZoomOutCommand { get; private set; }
443 | ///
444 | /// Resets the zoom level to the default. (100%)
445 | ///
446 | /// The zoom reset command.
447 | public ICommand ZoomResetCommand { get; private set; }
448 | ///
449 | /// Opens up a new program window (using the default text editor) where the source code of the currently displayed web
450 | /// page is shown.
451 | ///
452 | /// The view source command.
453 | public ICommand ViewSourceCommand { get; private set; }
454 | ///
455 | /// Command which cleans up the Resources used by the ChromiumWebBrowser
456 | ///
457 | /// The cleanup command.
458 | public ICommand CleanupCommand { get; private set; }
459 | ///
460 | /// Stops loading the current page.
461 | ///
462 | /// The stop command.
463 | public ICommand StopCommand { get; private set; }
464 | ///
465 | /// Cut selected text to the clipboard.
466 | ///
467 | /// The cut command.
468 | public ICommand CutCommand { get; private set; }
469 | ///
470 | /// Copy selected text to the clipboard.
471 | ///
472 | /// The copy command.
473 | public ICommand CopyCommand { get; private set; }
474 | ///
475 | /// Paste text from the clipboard.
476 | ///
477 | /// The paste command.
478 | public ICommand PasteCommand { get; private set; }
479 | ///
480 | /// Select all text.
481 | ///
482 | /// The select all command.
483 | public ICommand SelectAllCommand { get; private set; }
484 | ///
485 | /// Undo last action.
486 | ///
487 | /// The undo command.
488 | public ICommand UndoCommand { get; private set; }
489 | ///
490 | /// Redo last action.
491 | ///
492 | /// The redo command.
493 | public ICommand RedoCommand { get; private set; }
494 |
495 | ///
496 | /// Used as workaround for issue https://github.com/cefsharp/CefSharp/issues/3021
497 | ///
498 | private int canExecuteJavascriptInMainFrameChildProcessId;
499 |
500 | ///
501 | /// A flag that indicates if you can execute javascript in the main frame.
502 | /// Flag is set to true in IRenderProcessMessageHandler.OnContextCreated.
503 | /// and false in IRenderProcessMessageHandler.OnContextReleased
504 | ///
505 | public bool CanExecuteJavascriptInMainFrame { get; private set; }
506 |
507 | ///
508 | /// Initializes static members of the class.
509 | ///
510 | //TODO: Currently only a single Dispatcher is supported, all controls will
511 | //be Shutdown on that dispatcher which may not actually be the correct Dispatcher.
512 | //We could potentially use a ThreadStatic variable to implement this behaviour
513 | //and support multiple dispatchers.
514 | static ChromiumWebBrowser()
515 | {
516 | if (CefSharpSettings.ShutdownOnExit)
517 | {
518 | //Use Dispatcher.FromThread as it returns null if no dispatcher
519 | //is available for this thread.
520 | var dispatcher = Dispatcher.FromThread(Thread.CurrentThread);
521 | if (dispatcher == null)
522 | {
523 | //No dispatcher then we'll rely on Application.Exit
524 | var app = Application.Current;
525 |
526 | if (app != null)
527 | {
528 | app.Exit += OnApplicationExit;
529 | }
530 | }
531 | else
532 | {
533 | dispatcher.ShutdownStarted += DispatcherShutdownStarted;
534 | dispatcher.ShutdownFinished += DispatcherShutdownFinished;
535 | }
536 | }
537 | }
538 |
539 | ///
540 | /// Handles Dispatcher Shutdown
541 | ///
542 | /// sender
543 | /// eventargs
544 | private static void DispatcherShutdownStarted(object sender, EventArgs e)
545 | {
546 | var dispatcher = (Dispatcher)sender;
547 |
548 | dispatcher.ShutdownStarted -= DispatcherShutdownStarted;
549 |
550 | if (!DesignMode)
551 | {
552 | CefPreShutdown();
553 | }
554 | }
555 |
556 | private static void DispatcherShutdownFinished(object sender, EventArgs e)
557 | {
558 | var dispatcher = (Dispatcher)sender;
559 |
560 | dispatcher.ShutdownFinished -= DispatcherShutdownFinished;
561 |
562 | if (!DesignMode)
563 | {
564 | CefShutdown();
565 | }
566 | }
567 |
568 | ///
569 | /// Handles the event.
570 | ///
571 | /// The sender.
572 | /// The instance containing the event data.
573 | private static void OnApplicationExit(object sender, ExitEventArgs e)
574 | {
575 | if (!DesignMode)
576 | {
577 | CefShutdown();
578 | }
579 | }
580 |
581 | ///
582 | /// Required for designer support - this method cannot be inlined as the designer
583 | /// will attempt to load libcef.dll and will subsequently throw an exception.
584 | ///
585 | [MethodImpl(MethodImplOptions.NoInlining)]
586 | private static void CefPreShutdown()
587 | {
588 | Cef.PreShutdown();
589 | }
590 |
591 | ///
592 | /// Required for designer support - this method cannot be inlined as the designer
593 | /// will attempt to load libcef.dll and will subsiquently throw an exception.
594 | ///
595 | [MethodImpl(MethodImplOptions.NoInlining)]
596 | private static void CefShutdown()
597 | {
598 | Cef.Shutdown();
599 | }
600 |
601 | ///
602 | /// Initializes a new instance of the instance.
603 | ///
604 | /// Cef::Initialize() failed
605 | public ChromiumWebBrowser()
606 | {
607 | DesignMode = System.ComponentModel.DesignerProperties.GetIsInDesignMode(this);
608 |
609 | if (!DesignMode)
610 | {
611 | NoInliningConstructor();
612 | }
613 | }
614 |
615 | ///
616 | /// Initializes a new instance of the instance.
617 | ///
618 | /// address to load initially
619 | public ChromiumWebBrowser(string initialAddress)
620 | {
621 | this.initialAddress = initialAddress;
622 |
623 | NoInliningConstructor();
624 | }
625 |
626 | ///
627 | /// Constructor logic has been moved into this method
628 | /// Required for designer support - this method cannot be inlined as the designer
629 | /// will attempt to load libcef.dll and will subsiquently throw an exception.
630 | ///
631 | [MethodImpl(MethodImplOptions.NoInlining)]
632 | private void NoInliningConstructor()
633 | {
634 | InitializeCefInternal();
635 |
636 | //Add this ChromiumWebBrowser instance to a list of IDisposable objects
637 | // that if still alive at the time Cef.Shutdown is called will be disposed of
638 | // It's important all browser instances be freed before Cef.Shutdown is called.
639 | Cef.AddDisposable(this);
640 | Focusable = true;
641 | FocusVisualStyle = null;
642 |
643 | WebBrowser = this;
644 |
645 | SizeChanged += OnSizeChanged;
646 | IsVisibleChanged += OnIsVisibleChanged;
647 |
648 | BackCommand = new DelegateCommand(this.Back, () => CanGoBack);
649 | ForwardCommand = new DelegateCommand(this.Forward, () => CanGoForward);
650 | ReloadCommand = new DelegateCommand(this.Reload, () => !IsLoading);
651 | PrintCommand = new DelegateCommand(this.Print);
652 | ZoomInCommand = new DelegateCommand(ZoomIn);
653 | ZoomOutCommand = new DelegateCommand(ZoomOut);
654 | ZoomResetCommand = new DelegateCommand(ZoomReset);
655 | ViewSourceCommand = new DelegateCommand(this.ViewSource);
656 | CleanupCommand = new DelegateCommand(Dispose);
657 | StopCommand = new DelegateCommand(this.Stop);
658 | CutCommand = new DelegateCommand(this.Cut);
659 | CopyCommand = new DelegateCommand(this.Copy);
660 | PasteCommand = new DelegateCommand(this.Paste);
661 | SelectAllCommand = new DelegateCommand(this.SelectAll);
662 | UndoCommand = new DelegateCommand(this.Undo);
663 | RedoCommand = new DelegateCommand(this.Redo);
664 |
665 | managedCefBrowserAdapter = ManagedCefBrowserAdapter.Create(this, false);
666 |
667 | browserSettings = new BrowserSettings(autoDispose:true);
668 |
669 | PresentationSource.AddSourceChangedHandler(this, PresentationSourceChangedHandler);
670 |
671 | FocusHandler = new FocusHandler();
672 | LifeSpanHandler = new NoCloseLifespanHandler();
673 |
674 | UseLayoutRounding = true;
675 | }
676 |
677 | private void PresentationSourceChangedHandler(object sender, SourceChangedEventArgs args)
678 | {
679 | if (args.NewSource != null)
680 | {
681 | var source = (HwndSource)args.NewSource;
682 |
683 | var matrix = source.CompositionTarget.TransformToDevice;
684 |
685 | dpiScale = matrix.M11;
686 |
687 | var window = source.RootVisual as Window;
688 | if (window != null)
689 | {
690 | window.StateChanged += OnWindowStateChanged;
691 | window.LocationChanged += OnWindowLocationChanged;
692 | sourceWindow = window;
693 |
694 | if (CleanupElement == null)
695 | {
696 | CleanupElement = window;
697 | }
698 | else if (CleanupElement is Window parent)
699 | {
700 | //If the CleanupElement is a window then move it to the new Window
701 | if (parent != window)
702 | {
703 | CleanupElement = window;
704 | }
705 | }
706 | }
707 | }
708 | else if (args.OldSource != null)
709 | {
710 | var window = args.OldSource.RootVisual as Window;
711 | if (window != null)
712 | {
713 | window.StateChanged -= OnWindowStateChanged;
714 | window.LocationChanged -= OnWindowLocationChanged;
715 | sourceWindow = null;
716 | }
717 | }
718 | }
719 |
720 | ///
721 | protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi)
722 | {
723 | dpiScale = newDpi.DpiScaleX;
724 |
725 | //If the DPI changed then we need to resize.
726 | ResizeBrowser((int)ActualWidth, (int)ActualHeight);
727 |
728 | base.OnDpiChanged(oldDpi, newDpi);
729 | }
730 |
731 | private void OnSizeChanged(object sender, SizeChangedEventArgs e)
732 | {
733 | ResizeBrowser((int)e.NewSize.Width, (int)e.NewSize.Height);
734 | }
735 |
736 | ///
737 | protected override HandleRef BuildWindowCore(HandleRef hwndParent)
738 | {
739 | if (hwndHost == IntPtr.Zero)
740 | {
741 | hwndHost = CreateWindowEx(0, "static", "",
742 | WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN,
743 | 0, 0,
744 | (int)ActualWidth, (int)ActualHeight,
745 | hwndParent.Handle,
746 | (IntPtr)HOST_ID,
747 | IntPtr.Zero,
748 | 0);
749 | }
750 |
751 | CreateBrowser();
752 |
753 | if (browserSettings.AutoDispose)
754 | {
755 | browserSettings.Dispose();
756 | }
757 |
758 | browserSettings = null;
759 |
760 | // When CEF is integrated into main message loop
761 | // We unregister the WPF keyboard KeyboardInputSite to make sure
762 | // TAB works correctly
763 | if (Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
764 | {
765 | var keyboardInputSite = ((IKeyboardInputSink)this).KeyboardInputSite;
766 | if (keyboardInputSite != null)
767 | {
768 | ((IKeyboardInputSink)this).KeyboardInputSite = null;
769 |
770 | keyboardInputSite.Unregister();
771 | }
772 | }
773 |
774 | return new HandleRef(null, hwndHost);
775 | }
776 |
777 | ///
778 | protected override void DestroyWindowCore(HandleRef hwnd)
779 | {
780 | DestroyWindow(hwnd.Handle);
781 | }
782 |
783 | ///
784 | protected override bool TabIntoCore(TraversalRequest request)
785 | {
786 | if(InternalIsBrowserInitialized())
787 | {
788 | var host = browser.GetHost();
789 | host.SetFocus(true);
790 |
791 | return true;
792 | }
793 |
794 | return base.TabIntoCore(request);
795 | }
796 |
797 | ///
798 | protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
799 | {
800 | if(!e.Handled)
801 | {
802 | if (InternalIsBrowserInitialized())
803 | {
804 | var host = browser.GetHost();
805 | host.SetFocus(true);
806 | }
807 | else
808 | {
809 | initialFocus = true;
810 | }
811 | }
812 |
813 | base.OnGotKeyboardFocus(e);
814 | }
815 |
816 | ///
817 | protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
818 | {
819 | if (!e.Handled)
820 | {
821 | if (InternalIsBrowserInitialized())
822 | {
823 | var host = browser.GetHost();
824 | host.SetFocus(false);
825 | }
826 | else
827 | {
828 | initialFocus = false;
829 | }
830 | }
831 |
832 | base.OnLostKeyboardFocus(e);
833 | }
834 |
835 |
836 | ///
837 | protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
838 | {
839 | const int WM_SETFOCUS = 0x0007;
840 | const int WM_MOUSEACTIVATE = 0x0021;
841 | switch (msg)
842 | {
843 | case WM_SETFOCUS:
844 | case WM_MOUSEACTIVATE:
845 | {
846 | if(InternalIsBrowserInitialized())
847 | {
848 | var host = browser.GetHost();
849 | host.SetFocus(true);
850 |
851 | handled = true;
852 |
853 | return IntPtr.Zero;
854 | }
855 | break;
856 | }
857 | }
858 | return base.WndProc(hwnd, msg, wParam, lParam, ref handled);
859 | }
860 |
861 | ///
862 | /// If not in design mode; Releases unmanaged and - optionally - managed resources for the
863 | ///
864 | /// to release both managed and unmanaged resources; to release only unmanaged resources.
865 | protected override void Dispose(bool disposing)
866 | {
867 | // Attempt to move the disposeSignaled state from 0 to 1. If successful, we can be assured that
868 | // this thread is the first thread to do so, and can safely dispose of the object.
869 | if (Interlocked.CompareExchange(ref disposeSignaled, 1, 0) != 0)
870 | {
871 | return;
872 | }
873 |
874 | if (!DesignMode)
875 | {
876 | InternalDispose(disposing);
877 | }
878 |
879 | base.Dispose(disposing);
880 | }
881 |
882 | ///
883 | /// Releases unmanaged and - optionally - managed resources for the
884 | ///
885 | /// to release both managed and unmanaged resources; to release only unmanaged resources.
886 | ///
887 | /// This method cannot be inlined as the designer will attempt to load libcef.dll and will subsiquently throw an exception.
888 | ///
889 | [MethodImpl(MethodImplOptions.NoInlining)]
890 | private void InternalDispose(bool disposing)
891 | {
892 | Interlocked.Exchange(ref browserInitialized, 0);
893 |
894 | if (disposing)
895 | {
896 | SizeChanged -= OnSizeChanged;
897 | IsVisibleChanged -= OnIsVisibleChanged;
898 |
899 | PresentationSource.RemoveSourceChangedHandler(this, PresentationSourceChangedHandler);
900 | // Release window event listeners if PresentationSourceChangedHandler event wasn't
901 | // fired before Dispose
902 | if (sourceWindow != null)
903 | {
904 | sourceWindow.StateChanged -= OnWindowStateChanged;
905 | sourceWindow.LocationChanged -= OnWindowLocationChanged;
906 | sourceWindow = null;
907 | }
908 |
909 |
910 | UiThreadRunAsync(() =>
911 | {
912 | OnIsBrowserInitializedChanged(true, false);
913 |
914 | //To Minic the WPF behaviour this happens after OnIsBrowserInitializedChanged
915 | IsBrowserInitializedChanged?.Invoke(this, EventArgs.Empty);
916 |
917 | WebBrowser = null;
918 | });
919 |
920 | // Don't maintain a reference to event listeners anylonger:
921 | ConsoleMessage = null;
922 | FrameLoadEnd = null;
923 | FrameLoadStart = null;
924 | IsBrowserInitializedChanged = null;
925 | LoadError = null;
926 | LoadingStateChanged = null;
927 | StatusMessage = null;
928 | TitleChanged = null;
929 | JavascriptMessageReceived = null;
930 |
931 | // Release reference to handlers, except LifeSpanHandler which we don't set to null
932 | //so that ILifeSpanHandler.DoClose will not be invoked. Previously we set LifeSpanHandler = null
933 | //after managedCefBrowserAdapter.Dispose, there's still cases where the LifeSpanHandler was null
934 | //when DoClose was called Issue https://github.com/cefsharp/CefSharp.Wpf.HwndHost/issues/10
935 | FindHandler = null;
936 | DialogHandler = null;
937 | RequestHandler = null;
938 | DisplayHandler = null;
939 | LoadHandler = null;
940 | KeyboardHandler = null;
941 | JsDialogHandler = null;
942 | DragHandler = null;
943 | DownloadHandler = null;
944 | MenuHandler = null;
945 | FocusHandler = null;
946 | ResourceRequestHandlerFactory = null;
947 | RenderProcessMessageHandler = null;
948 |
949 | this.DisposeDevToolsContext();
950 |
951 | browser = null;
952 | BrowserCore = null;
953 |
954 | if (CleanupElement != null)
955 | {
956 | CleanupElement.Unloaded -= OnCleanupElementUnloaded;
957 | }
958 |
959 | managedCefBrowserAdapter?.Dispose();
960 | managedCefBrowserAdapter = null;
961 | }
962 |
963 | Cef.RemoveDisposable(this);
964 | }
965 |
966 | ///
967 | /// Sets the address.
968 | ///
969 | /// The instance containing the event data.
970 | void IWebBrowserInternal.SetAddress(AddressChangedEventArgs args)
971 | {
972 | UiThreadRunAsync(() =>
973 | {
974 | ignoreUriChange = true;
975 | SetCurrentValue(AddressProperty, args.Address);
976 | ignoreUriChange = false;
977 |
978 | // The tooltip should obviously also be reset (and hidden) when the address changes.
979 | SetCurrentValue(TooltipTextProperty, null);
980 | });
981 | }
982 |
983 | ///
984 | /// Sets the loading state change.
985 | ///
986 | /// The instance containing the event data.
987 | void IWebBrowserInternal.SetLoadingStateChange(LoadingStateChangedEventArgs args)
988 | {
989 | if (removeExNoActivateStyle && InternalIsBrowserInitialized())
990 | {
991 | removeExNoActivateStyle = false;
992 |
993 | var host = this.GetBrowserHost();
994 | var hwnd = host.GetWindowHandle();
995 | //Remove the WS_EX_NOACTIVATE style so that future mouse clicks inside the
996 | //browser correctly activate and focus the browser.
997 | //https://github.com/chromiumembedded/cef/blob/9df4a54308a88fd80c5774d91c62da35afb5fd1b/tests/cefclient/browser/root_window_win.cc#L1088
998 | RemoveExNoActivateStyle(hwnd);
999 | }
1000 |
1001 | UiThreadRunAsync(() =>
1002 | {
1003 | SetCurrentValue(CanGoBackProperty, args.CanGoBack);
1004 | SetCurrentValue(CanGoForwardProperty, args.CanGoForward);
1005 | SetCurrentValue(IsLoadingProperty, args.IsLoading);
1006 |
1007 | ((DelegateCommand)BackCommand).RaiseCanExecuteChanged();
1008 | ((DelegateCommand)ForwardCommand).RaiseCanExecuteChanged();
1009 | ((DelegateCommand)ReloadCommand).RaiseCanExecuteChanged();
1010 | });
1011 |
1012 | LoadingStateChanged?.Invoke(this, args);
1013 |
1014 | initialLoadAction?.Invoke(args.IsLoading, null);
1015 | }
1016 |
1017 | ///
1018 | /// Sets the title.
1019 | ///
1020 | /// The instance containing the event data.
1021 | void IWebBrowserInternal.SetTitle(TitleChangedEventArgs args)
1022 | {
1023 | UiThreadRunAsync(() => SetCurrentValue(TitleProperty, args.Title));
1024 | }
1025 |
1026 | ///
1027 | /// Sets the tooltip text.
1028 | ///
1029 | /// The tooltip text.
1030 | void IWebBrowserInternal.SetTooltipText(string tooltipText)
1031 | {
1032 | UiThreadRunAsync(() => SetCurrentValue(TooltipTextProperty, tooltipText));
1033 | }
1034 |
1035 | ///
1036 | /// Handles the event.
1037 | ///
1038 | /// The instance containing the event data.
1039 | void IWebBrowserInternal.OnFrameLoadStart(FrameLoadStartEventArgs args)
1040 | {
1041 | FrameLoadStart?.Invoke(this, args);
1042 | }
1043 |
1044 | ///
1045 | /// Handles the event.
1046 | ///
1047 | /// The instance containing the event data.
1048 | void IWebBrowserInternal.OnFrameLoadEnd(FrameLoadEndEventArgs args)
1049 | {
1050 | FrameLoadEnd?.Invoke(this, args);
1051 | }
1052 |
1053 | ///
1054 | /// Handles the event.
1055 | ///
1056 | /// The instance containing the event data.
1057 | void IWebBrowserInternal.OnConsoleMessage(ConsoleMessageEventArgs args)
1058 | {
1059 | ConsoleMessage?.Invoke(this, args);
1060 | }
1061 |
1062 | ///
1063 | /// Handles the event.
1064 | ///
1065 | /// The instance containing the event data.
1066 | void IWebBrowserInternal.OnStatusMessage(StatusMessageEventArgs args)
1067 | {
1068 | StatusMessage?.Invoke(this, args);
1069 | }
1070 |
1071 | ///
1072 | /// Handles the event.
1073 | ///
1074 | /// The instance containing the event data.
1075 | void IWebBrowserInternal.OnLoadError(LoadErrorEventArgs args)
1076 | {
1077 | LoadError?.Invoke(this, args);
1078 |
1079 | initialLoadAction?.Invoke(null, args.ErrorCode);
1080 | }
1081 |
1082 | void IWebBrowserInternal.SetCanExecuteJavascriptOnMainFrame(string frameId, bool canExecute)
1083 | {
1084 | //When loading pages of a different origin the frameId changes
1085 | //For the first loading of a new origin the messages from the render process
1086 | //Arrive in a different order than expected, the OnContextCreated message
1087 | //arrives before the OnContextReleased, then the message for OnContextReleased
1088 | //incorrectly overrides the value
1089 | //https://github.com/cefsharp/CefSharp/issues/3021
1090 |
1091 | var chromiumChildProcessId = GetChromiumChildProcessId(frameId);
1092 |
1093 | if (chromiumChildProcessId > canExecuteJavascriptInMainFrameChildProcessId && !canExecute)
1094 | {
1095 | return;
1096 | }
1097 |
1098 | canExecuteJavascriptInMainFrameChildProcessId = chromiumChildProcessId;
1099 | CanExecuteJavascriptInMainFrame = canExecute;
1100 | }
1101 |
1102 | void IWebBrowserInternal.SetJavascriptMessageReceived(JavascriptMessageReceivedEventArgs args)
1103 | {
1104 | JavascriptMessageReceived?.Invoke(this, args);
1105 | }
1106 |
1107 | ///
1108 | /// Gets the browser adapter.
1109 | ///
1110 | /// The browser adapter.
1111 | IBrowserAdapter IWebBrowserInternal.BrowserAdapter
1112 | {
1113 | get { return managedCefBrowserAdapter; }
1114 | }
1115 |
1116 | ///
1117 | /// Gets or sets a value indicating whether this instance has parent.
1118 | ///
1119 | /// true if this instance has parent; otherwise, false.
1120 | bool IWebBrowserInternal.HasParent { get; set; }
1121 |
1122 | ///
1123 | /// Called when [after browser created].
1124 | ///
1125 | /// The browser.
1126 | void IWebBrowserInternal.OnAfterBrowserCreated(IBrowser browser)
1127 | {
1128 | if (IsDisposed || browser.IsDisposed)
1129 | {
1130 | return;
1131 | }
1132 |
1133 | this.browser = browser;
1134 | BrowserCore = browser;
1135 | initialLoadAction = InitialLoad;
1136 | Interlocked.Exchange(ref browserInitialized, 1);
1137 |
1138 | UiThreadRunAsync(() =>
1139 | {
1140 | if (!IsDisposed)
1141 | {
1142 | OnIsBrowserInitializedChanged(false, true);
1143 | //To Minic the WPF behaviour this happens after OnIsBrowserInitializedChanged
1144 | IsBrowserInitializedChanged?.Invoke(this, EventArgs.Empty);
1145 |
1146 | // Only call Load if initialAddress is null and Address is not empty
1147 | if (string.IsNullOrEmpty(initialAddress) && !string.IsNullOrEmpty(Address) && !initialLoadCalled)
1148 | {
1149 | Load(Address);
1150 | }
1151 | }
1152 | });
1153 |
1154 | ResizeBrowser((int)ActualWidth, (int)ActualHeight);
1155 |
1156 | if (initialFocus)
1157 | {
1158 | browser.GetHost()?.SetFocus(true);
1159 | }
1160 | }
1161 |
1162 | ///
1163 | /// A flag that indicates whether the state of the control current supports the GoBack action (true) or not (false).
1164 | ///
1165 | /// true if this instance can go back; otherwise, false.
1166 | /// In the WPF control, this property is implemented as a Dependency Property and fully supports data
1167 | /// binding.
1168 | public bool CanGoBack
1169 | {
1170 | get { return (bool)GetValue(CanGoBackProperty); }
1171 | }
1172 |
1173 | ///
1174 | /// The can go back property
1175 | ///
1176 | public static DependencyProperty CanGoBackProperty = DependencyProperty.Register(nameof(CanGoBack), typeof(bool), typeof(ChromiumWebBrowser));
1177 |
1178 | ///
1179 | /// A flag that indicates whether the state of the control currently supports the GoForward action (true) or not (false).
1180 | ///
1181 | /// true if this instance can go forward; otherwise, false.
1182 | /// In the WPF control, this property is implemented as a Dependency Property and fully supports data
1183 | /// binding.
1184 | public bool CanGoForward
1185 | {
1186 | get { return (bool)GetValue(CanGoForwardProperty); }
1187 | }
1188 |
1189 | ///
1190 | /// The can go forward property
1191 | ///
1192 | public static DependencyProperty CanGoForwardProperty = DependencyProperty.Register(nameof(CanGoForward), typeof(bool), typeof(ChromiumWebBrowser));
1193 |
1194 | ///
1195 | /// The address (URL) which the browser control is currently displaying.
1196 | /// Will automatically be updated as the user navigates to another page (e.g. by clicking on a link).
1197 | ///
1198 | /// The address.
1199 | /// In the WPF control, this property is implemented as a Dependency Property and fully supports data
1200 | /// binding.
1201 | public string Address
1202 | {
1203 | get { return (string)GetValue(AddressProperty); }
1204 | set { SetValue(AddressProperty, value); }
1205 | }
1206 |
1207 | ///
1208 | /// The address property
1209 | ///
1210 | public static readonly DependencyProperty AddressProperty =
1211 | DependencyProperty.Register(nameof(Address), typeof(string), typeof(ChromiumWebBrowser),
1212 | new UIPropertyMetadata(null, OnAddressChanged));
1213 |
1214 | ///
1215 | /// Handles the event.
1216 | ///
1217 | /// The sender.
1218 | /// The instance containing the event data.
1219 | private static void OnAddressChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
1220 | {
1221 | var owner = (ChromiumWebBrowser)sender;
1222 | var oldValue = (string)args.OldValue;
1223 | var newValue = (string)args.NewValue;
1224 |
1225 | owner.OnAddressChanged(oldValue, newValue);
1226 | }
1227 |
1228 | ///
1229 | /// Called when [address changed].
1230 | ///
1231 | /// The old value.
1232 | /// The new value.
1233 | protected virtual void OnAddressChanged(string oldValue, string newValue)
1234 | {
1235 | if (ignoreUriChange || newValue == null)
1236 | {
1237 | return;
1238 | }
1239 |
1240 | Load(newValue);
1241 | }
1242 |
1243 | ///
1244 | /// A flag that indicates whether the control is currently loading one or more web pages (true) or not (false).
1245 | ///
1246 | /// true if this instance is loading; otherwise, false.
1247 | /// In the WPF control, this property is implemented as a Dependency Property and fully supports data
1248 | /// binding.
1249 | public bool IsLoading
1250 | {
1251 | get { return (bool)GetValue(IsLoadingProperty); }
1252 | }
1253 |
1254 | ///
1255 | /// The is loading property
1256 | ///
1257 | public static readonly DependencyProperty IsLoadingProperty =
1258 | DependencyProperty.Register(nameof(IsLoading), typeof(bool), typeof(ChromiumWebBrowser), new PropertyMetadata(false));
1259 |
1260 | ///
1261 | /// A flag that indicates whether the WebBrowser is initialized (true) or not (false).
1262 | ///
1263 | /// true if this instance is browser initialized; otherwise, false.
1264 | /// In the WPF control, this property is implemented as a Dependency Property and fully supports data
1265 | /// binding.
1266 | public bool IsBrowserInitialized
1267 | {
1268 | get { return InternalIsBrowserInitialized(); }
1269 | }
1270 |
1271 | ///
1272 | /// Event called after the underlying CEF browser instance has been created and
1273 | /// when the instance has been Disposed.
1274 | /// will be true when the underlying CEF Browser
1275 | /// has been created and false when the browser is being Disposed.
1276 | ///
1277 | public event EventHandler IsBrowserInitializedChanged;
1278 |
1279 | ///
1280 | /// Called when [is browser initialized changed].
1281 | ///
1282 | /// if set to true [old value].
1283 | /// if set to true [new value].
1284 | protected virtual void OnIsBrowserInitializedChanged(bool oldValue, bool newValue)
1285 | {
1286 | if (newValue && !IsDisposed)
1287 | {
1288 | var task = this.GetZoomLevelAsync();
1289 | task.ContinueWith(previous =>
1290 | {
1291 | if (previous.Status == TaskStatus.RanToCompletion)
1292 | {
1293 | UiThreadRunAsync(() =>
1294 | {
1295 | if (!IsDisposed)
1296 | {
1297 | SetCurrentValue(ZoomLevelProperty, previous.Result);
1298 | }
1299 | });
1300 | }
1301 | else
1302 | {
1303 | throw new InvalidOperationException("Unexpected failure of calling CEF->GetZoomLevelAsync", previous.Exception);
1304 | }
1305 | }, TaskContinuationOptions.ExecuteSynchronously);
1306 | }
1307 | }
1308 |
1309 | ///
1310 | /// The title of the web page being currently displayed.
1311 | ///
1312 | /// The title.
1313 | /// This property is implemented as a Dependency Property and fully supports data binding.
1314 | public string Title
1315 | {
1316 | get { return (string)GetValue(TitleProperty); }
1317 | set { SetValue(TitleProperty, value); }
1318 | }
1319 |
1320 | ///
1321 | /// The title property
1322 | ///
1323 | public static readonly DependencyProperty TitleProperty =
1324 | DependencyProperty.Register(nameof(Title), typeof(string), typeof(ChromiumWebBrowser), new PropertyMetadata(null, OnTitleChanged));
1325 |
1326 | ///
1327 | /// Event handler that will get called when the browser title changes
1328 | ///
1329 | public event DependencyPropertyChangedEventHandler TitleChanged;
1330 |
1331 | ///
1332 | /// Handles the event.
1333 | ///
1334 | /// The d.
1335 | /// The instance containing the event data.
1336 | private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
1337 | {
1338 | var owner = (ChromiumWebBrowser)d;
1339 |
1340 | owner.TitleChanged?.Invoke(owner, e);
1341 | }
1342 |
1343 | ///
1344 | /// The zoom level at which the browser control is currently displaying.
1345 | /// Can be set to 0 to clear the zoom level (resets to default zoom level).
1346 | ///
1347 | /// The zoom level.
1348 | public double ZoomLevel
1349 | {
1350 | get { return (double)GetValue(ZoomLevelProperty); }
1351 | set { SetValue(ZoomLevelProperty, value); }
1352 | }
1353 |
1354 | ///
1355 | /// The zoom level property
1356 | ///
1357 | public static readonly DependencyProperty ZoomLevelProperty =
1358 | DependencyProperty.Register(nameof(ZoomLevel), typeof(double), typeof(ChromiumWebBrowser),
1359 | new UIPropertyMetadata(0d, OnZoomLevelChanged));
1360 |
1361 | ///
1362 | /// Handles the event.
1363 | ///
1364 | /// The sender.
1365 | /// The instance containing the event data.
1366 | private static void OnZoomLevelChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
1367 | {
1368 | var owner = (ChromiumWebBrowser)sender;
1369 | var oldValue = (double)args.OldValue;
1370 | var newValue = (double)args.NewValue;
1371 |
1372 | owner.OnZoomLevelChanged(oldValue, newValue);
1373 | }
1374 |
1375 | ///
1376 | /// Called when [zoom level changed].
1377 | ///
1378 | /// The old value.
1379 | /// The new value.
1380 | protected virtual void OnZoomLevelChanged(double oldValue, double newValue)
1381 | {
1382 | this.SetZoomLevel(newValue);
1383 | }
1384 |
1385 | ///
1386 | /// Specifies the amount used to increase/decrease to ZoomLevel by
1387 | /// By Default this value is 0.10
1388 | ///
1389 | /// The zoom level increment.
1390 | public double ZoomLevelIncrement
1391 | {
1392 | get { return (double)GetValue(ZoomLevelIncrementProperty); }
1393 | set { SetValue(ZoomLevelIncrementProperty, value); }
1394 | }
1395 |
1396 | ///
1397 | /// The zoom level increment property
1398 | ///
1399 | public static readonly DependencyProperty ZoomLevelIncrementProperty =
1400 | DependencyProperty.Register(nameof(ZoomLevelIncrement), typeof(double), typeof(ChromiumWebBrowser), new PropertyMetadata(0.10));
1401 |
1402 | ///
1403 | /// The CleanupElement controls when the Browser will be Disposed.
1404 | /// The will be Disposed when is called.
1405 | /// Be aware that this Control is not usable anymore after it has been disposed.
1406 | ///
1407 | /// The cleanup element.
1408 | public FrameworkElement CleanupElement
1409 | {
1410 | get { return (FrameworkElement)GetValue(CleanupElementProperty); }
1411 | set { SetValue(CleanupElementProperty, value); }
1412 | }
1413 |
1414 | ///
1415 | /// The cleanup element property
1416 | ///
1417 | public static readonly DependencyProperty CleanupElementProperty =
1418 | DependencyProperty.Register(nameof(CleanupElement), typeof(FrameworkElement), typeof(ChromiumWebBrowser), new PropertyMetadata(null, OnCleanupElementChanged));
1419 |
1420 | ///
1421 | /// Handles the event.
1422 | ///
1423 | /// The sender.
1424 | /// The instance containing the event data.
1425 | private static void OnCleanupElementChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
1426 | {
1427 | var owner = (ChromiumWebBrowser)sender;
1428 | var oldValue = (FrameworkElement)args.OldValue;
1429 | var newValue = (FrameworkElement)args.NewValue;
1430 |
1431 | owner.OnCleanupElementChanged(oldValue, newValue);
1432 | }
1433 |
1434 | ///
1435 | /// Called when [cleanup element changed].
1436 | ///
1437 | /// The old value.
1438 | /// The new value.
1439 | protected virtual void OnCleanupElementChanged(FrameworkElement oldValue, FrameworkElement newValue)
1440 | {
1441 | if (oldValue != null)
1442 | {
1443 | oldValue.Unloaded -= OnCleanupElementUnloaded;
1444 | }
1445 |
1446 | if (newValue != null)
1447 | {
1448 | newValue.Unloaded += OnCleanupElementUnloaded;
1449 | }
1450 | }
1451 |
1452 |
1453 | ///
1454 | /// Handles the event.
1455 | ///
1456 | /// The sender.
1457 | /// The instance containing the event data.
1458 | private void OnCleanupElementUnloaded(object sender, RoutedEventArgs e)
1459 | {
1460 | Dispose();
1461 | }
1462 |
1463 | ///
1464 | /// The text that will be displayed as a ToolTip
1465 | ///
1466 | /// The tooltip text.
1467 | public string TooltipText
1468 | {
1469 | get { return (string)GetValue(TooltipTextProperty); }
1470 | }
1471 |
1472 | ///
1473 | /// The tooltip text property
1474 | ///
1475 | public static readonly DependencyProperty TooltipTextProperty =
1476 | DependencyProperty.Register(nameof(TooltipText), typeof(string), typeof(ChromiumWebBrowser));
1477 |
1478 | ///
1479 | /// Gets or sets the WebBrowser.
1480 | ///
1481 | /// The WebBrowser.
1482 | public IWebBrowser WebBrowser
1483 | {
1484 | get { return (IWebBrowser)GetValue(WebBrowserProperty); }
1485 | set { SetValue(WebBrowserProperty, value); }
1486 | }
1487 |
1488 | ///
1489 | /// The WebBrowser property
1490 | ///
1491 | public static readonly DependencyProperty WebBrowserProperty =
1492 | DependencyProperty.Register(nameof(WebBrowser), typeof(IWebBrowser), typeof(ChromiumWebBrowser), new UIPropertyMetadata(defaultValue: null));
1493 |
1494 | ///
1495 | /// Override this method to handle creation of WindowInfo. This method can be used to customise aspects of
1496 | /// browser creation including configuration of settings such as .
1497 | /// Window Activation is disabled by default, you can re-enable it by overriding and removing the
1498 | /// WS_EX_NOACTIVATE style from .
1499 | ///
1500 | /// Window handle for the Control
1501 | /// Window Info
1502 | ///
1503 | /// To re-enable Window Activation then remove WS_EX_NOACTIVATE from ExStyle
1504 | ///
1505 | /// const uint WS_EX_NOACTIVATE = 0x08000000;
1506 | /// windowInfo.ExStyle &= ~WS_EX_NOACTIVATE;
1507 | ///
1508 | ///
1509 | protected virtual IWindowInfo CreateBrowserWindowInfo(IntPtr handle)
1510 | {
1511 | var windowInfo = Core.ObjectFactory.CreateWindowInfo();
1512 | windowInfo.RuntimeStyle = CefSharpSettings.RuntimeStyle ?? CefRuntimeStyle.Alloy;
1513 | windowInfo.SetAsChild(handle);
1514 |
1515 | if (!ActivateBrowserOnCreation)
1516 | {
1517 | //Disable Window activation by default
1518 | //https://bitbucket.org/chromiumembedded/cef/issues/1856/branch-2526-cef-activates-browser-window
1519 | windowInfo.ExStyle |= WS_EX_NOACTIVATE;
1520 | }
1521 |
1522 | return windowInfo;
1523 | }
1524 |
1525 | [MethodImpl(MethodImplOptions.NoInlining)]
1526 | private void CreateBrowser()
1527 | {
1528 | browserCreated = true;
1529 |
1530 | if (((IWebBrowserInternal)this).HasParent == false)
1531 | {
1532 | var windowInfo = CreateBrowserWindowInfo(hwndHost);
1533 |
1534 | //We actually check if WS_EX_NOACTIVATE was set for instances
1535 | //the user has override CreateBrowserWindowInfo and not called base.CreateBrowserWindowInfo
1536 | removeExNoActivateStyle = (windowInfo.ExStyle & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE;
1537 |
1538 | //If initialAddress is set then we use that value, later in OnAfterBrowserCreated then we will
1539 | //call Load(url) if initial address was empty.
1540 | managedCefBrowserAdapter.CreateBrowser(windowInfo, browserSettings as BrowserSettings, requestContext as RequestContext, initialAddress);
1541 | }
1542 | }
1543 |
1544 | ///
1545 | /// Runs the specific Action on the Dispatcher in an async fashion
1546 | ///
1547 | /// The action.
1548 | /// The priority.
1549 | internal void UiThreadRunAsync(Action action, DispatcherPriority priority = DispatcherPriority.DataBind)
1550 | {
1551 | if (Dispatcher.CheckAccess())
1552 | {
1553 | action();
1554 | }
1555 | else if (!Dispatcher.HasShutdownStarted)
1556 | {
1557 | Dispatcher.BeginInvoke(action, priority);
1558 | }
1559 | }
1560 |
1561 | ///
1562 | /// Handles the event.
1563 | ///
1564 | /// The sender.
1565 | /// The instance containing the event data.
1566 | private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs args)
1567 | {
1568 | var isVisible = (bool)args.NewValue;
1569 |
1570 | if (InternalIsBrowserInitialized())
1571 | {
1572 | var host = browser.GetHost();
1573 |
1574 | if (isVisible)
1575 | {
1576 | ResizeBrowser((int)ActualWidth, (int)ActualHeight);
1577 |
1578 | //Fix for #1778 - When browser becomes visible we update the zoom level
1579 | //browsers of the same origin will share the same zoomlevel and
1580 | //we need to track the update, so our ZoomLevelProperty works
1581 | //properly
1582 | host.GetZoomLevelAsync().ContinueWith(t =>
1583 | {
1584 | if (!IsDisposed)
1585 | {
1586 | SetCurrentValue(ZoomLevelProperty, t.Result);
1587 | }
1588 | },
1589 | CancellationToken.None,
1590 | TaskContinuationOptions.OnlyOnRanToCompletion,
1591 | TaskScheduler.FromCurrentSynchronizationContext());
1592 | }
1593 | else
1594 | {
1595 | //Hide browser
1596 | ResizeBrowser(0, 0);
1597 | }
1598 | }
1599 | }
1600 |
1601 | ///
1602 | /// Loads the specified URL.
1603 | ///
1604 | /// The URL to be loaded.
1605 | public void Load(string url)
1606 | {
1607 | if (IsDisposed)
1608 | {
1609 | return;
1610 | }
1611 |
1612 | //If the browser is already initialized then we can call LoadUrl directly
1613 | if (InternalIsBrowserInitialized())
1614 | {
1615 | var b = browser;
1616 | // Added null check -> binding-triggered changes of Address will lead to a nullref after Dispose has been called
1617 | if (b != null)
1618 | {
1619 | initialLoadCalled = true;
1620 |
1621 | using (var frame = b.MainFrame)
1622 | {
1623 | frame.LoadUrl(url);
1624 | }
1625 | }
1626 | }
1627 | //If CreateBrowser was called and InternalIsBrowserInitialized() == false then we need to set the Address
1628 | //property so in OnAfterBrowserCreated the Url is loaded. If initialAddress was
1629 | //set then the Url set here will be ignored. If we called Load(url) then historically
1630 | //an aborted error would be raised as per https://github.com/cefsharp/CefSharp/issues/2300
1631 | //So we ignore the call for now.
1632 | else if (browserCreated)
1633 | {
1634 | UiThreadRunAsync(() =>
1635 | {
1636 | Address = url;
1637 | });
1638 | }
1639 | //Before browser created, set the intialAddress
1640 | else
1641 | {
1642 | initialAddress = url;
1643 | }
1644 | }
1645 |
1646 | ///
1647 | public Task LoadUrlAsync(string url)
1648 | {
1649 | //LoadUrlAsync is actually a static method so that CefSharp.Wpf.HwndHost can reuse the code
1650 | //It's not actually an extension method so we can have it included as part of the
1651 | //IWebBrowser interface
1652 | return CefSharp.WebBrowserExtensions.LoadUrlAsync(this, url);
1653 | }
1654 |
1655 | ///
1656 | /// Zooms the browser in.
1657 | ///
1658 | private void ZoomIn()
1659 | {
1660 | UiThreadRunAsync(() =>
1661 | {
1662 | ZoomLevel = ZoomLevel + ZoomLevelIncrement;
1663 | });
1664 | }
1665 |
1666 | ///
1667 | /// Zooms the browser out.
1668 | ///
1669 | private void ZoomOut()
1670 | {
1671 | UiThreadRunAsync(() =>
1672 | {
1673 | ZoomLevel = ZoomLevel - ZoomLevelIncrement;
1674 | });
1675 | }
1676 |
1677 | ///
1678 | /// Reset the browser's zoom level to default.
1679 | ///
1680 | private void ZoomReset()
1681 | {
1682 | UiThreadRunAsync(() =>
1683 | {
1684 | ZoomLevel = 0;
1685 | });
1686 | }
1687 |
1688 | ///
1689 | /// The javascript object repository, one repository per ChromiumWebBrowser instance.
1690 | ///
1691 | public IJavascriptObjectRepository JavascriptObjectRepository
1692 | {
1693 | get { return managedCefBrowserAdapter?.JavascriptObjectRepository; }
1694 | }
1695 |
1696 | ///
1697 | public IBrowser BrowserCore { get; private set; }
1698 |
1699 | ///
1700 | /// Used by CefSharp.Puppeteer to associate a single DevToolsContext with a ChromiumWebBrowser instance.
1701 | ///
1702 | IDisposable IWebBrowserInternal.DevToolsContext { get; set; }
1703 |
1704 | ///
1705 | /// Returns the current IBrowser Instance
1706 | ///
1707 | /// browser instance or null
1708 | public IBrowser GetBrowser()
1709 | {
1710 | return browser;
1711 | }
1712 |
1713 | private static void InitializeCefInternal()
1714 | {
1715 | if (Cef.IsInitialized == null)
1716 | {
1717 | if (!Cef.Initialize(new CefSettings()))
1718 | {
1719 | throw new InvalidOperationException(CefInitializeFailedErrorMessage);
1720 | }
1721 | }
1722 |
1723 | if (Cef.IsInitialized == false)
1724 | {
1725 | throw new InvalidOperationException(CefIsInitializedFalseErrorMessage);
1726 | }
1727 | }
1728 |
1729 | ///
1730 | /// Check is browserisinitialized
1731 | ///
1732 | /// true if browser is initialized
1733 | private bool InternalIsBrowserInitialized()
1734 | {
1735 | // Use CompareExchange to read the current value - if disposeCount is 1, we set it to 1, effectively a no-op
1736 | // Volatile.Read would likely use a memory barrier which I believe is unnecessary in this scenario
1737 | return Interlocked.CompareExchange(ref browserInitialized, 0, 0) == 1;
1738 | }
1739 |
1740 | ///
1741 | /// Resizes the browser.
1742 | ///
1743 | private void ResizeBrowser(int width, int height)
1744 | {
1745 | if (InternalIsBrowserInitialized())
1746 | {
1747 | if (dpiScale > 1)
1748 | {
1749 | width = (int)Math.Round(width * dpiScale);
1750 | height = (int)Math.Round(height * dpiScale);
1751 | }
1752 |
1753 | managedCefBrowserAdapter.Resize(width, height);
1754 | }
1755 | }
1756 |
1757 | private void OnWindowStateChanged(object sender, EventArgs e)
1758 | {
1759 | var window = (Window)sender;
1760 |
1761 | switch (window.WindowState)
1762 | {
1763 | case WindowState.Normal:
1764 | case WindowState.Maximized:
1765 | {
1766 | if (previousWindowState == WindowState.Minimized && InternalIsBrowserInitialized())
1767 | {
1768 | ResizeBrowser((int)ActualWidth, (int)ActualHeight);
1769 | }
1770 | break;
1771 | }
1772 | case WindowState.Minimized:
1773 | {
1774 | if (InternalIsBrowserInitialized())
1775 | {
1776 | //Set the browser size to 0,0 to reduce CPU usage
1777 | ResizeBrowser(0, 0);
1778 | }
1779 | break;
1780 | }
1781 | }
1782 |
1783 | previousWindowState = window.WindowState;
1784 | }
1785 |
1786 | private void OnWindowLocationChanged(object sender, EventArgs e)
1787 | {
1788 | if (InternalIsBrowserInitialized())
1789 | {
1790 | var host = browser.GetHost();
1791 |
1792 | host.NotifyMoveOrResizeStarted();
1793 | }
1794 | }
1795 |
1796 | ///
1797 | public void LoadUrl(string url)
1798 | {
1799 | Load(url);
1800 | }
1801 |
1802 | ///
1803 | public Task WaitForInitialLoadAsync()
1804 | {
1805 | return initialLoadTaskCompletionSource.Task;
1806 | }
1807 |
1808 | private void InitialLoad(bool? isLoading, CefErrorCode? errorCode)
1809 | {
1810 | if (IsDisposed)
1811 | {
1812 | initialLoadAction = null;
1813 |
1814 | initialLoadTaskCompletionSource.TrySetCanceled();
1815 |
1816 | return;
1817 | }
1818 |
1819 | if (isLoading.HasValue)
1820 | {
1821 | if (isLoading.Value)
1822 | {
1823 | return;
1824 | }
1825 |
1826 | initialLoadAction = null;
1827 |
1828 | var host = browser?.GetHost();
1829 |
1830 | var navEntry = host?.GetVisibleNavigationEntry();
1831 |
1832 | int statusCode = navEntry?.HttpStatusCode ?? -1;
1833 |
1834 | //By default 0 is some sort of error, we map that to -1
1835 | //so that it's clearer that something failed.
1836 | if (statusCode == 0)
1837 | {
1838 | statusCode = -1;
1839 | }
1840 |
1841 | initialLoadTaskCompletionSource.TrySetResult(new LoadUrlAsyncResponse(CefErrorCode.None, statusCode));
1842 | }
1843 | else if (errorCode.HasValue)
1844 | {
1845 | //Actions that trigger a download will raise an aborted error.
1846 | //Generally speaking Aborted is safe to ignore
1847 | if (errorCode == CefErrorCode.Aborted)
1848 | {
1849 | return;
1850 | }
1851 |
1852 | initialLoadAction = null;
1853 |
1854 | initialLoadTaskCompletionSource.TrySetResult(new LoadUrlAsyncResponse(errorCode.Value, -1));
1855 | }
1856 | }
1857 |
1858 | ///
1859 | public bool TryGetBrowserCoreById(int browserId, out IBrowser browser)
1860 | {
1861 | var browserAdapter = managedCefBrowserAdapter;
1862 |
1863 | if (IsDisposed || browserAdapter == null || browserAdapter.IsDisposed)
1864 | {
1865 | browser = null;
1866 |
1867 | return false;
1868 | }
1869 |
1870 | browser = browserAdapter.GetBrowser(browserId);
1871 |
1872 | return browser != null;
1873 | }
1874 |
1875 | ///
1876 | public async Task GetContentSizeAsync()
1877 | {
1878 | ThrowExceptionIfDisposed();
1879 | ThrowExceptionIfBrowserNotInitialized();
1880 |
1881 | using (var devToolsClient = browser.GetDevToolsClient())
1882 | {
1883 | //Get the content size
1884 | var layoutMetricsResponse = await devToolsClient.Page.GetLayoutMetricsAsync().ConfigureAwait(continueOnCapturedContext: false);
1885 |
1886 | var rect = layoutMetricsResponse.CssContentSize;
1887 |
1888 | return new Structs.DomRect(rect.X, rect.Y, rect.Width, rect.Height);
1889 | }
1890 | }
1891 |
1892 | ///
1893 | public Task WaitForNavigationAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default)
1894 | {
1895 | //WaitForNavigationAsync is actually a static method so that CefSharp.Wpf.HwndHost can reuse the code
1896 | return CefSharp.WebBrowserExtensions.WaitForNavigationAsync(this, timeout, cancellationToken);
1897 | }
1898 |
1899 | ///
1900 | /// Capture page screenshot.
1901 | ///
1902 | /// Image compression format (defaults to png).
1903 | /// Compression quality from range [0..100] (jpeg only).
1904 | /// Capture the screenshot of a given region only.
1905 | /// Capture the screenshot from the surface, rather than the view. Defaults to true.
1906 | /// Capture the screenshot beyond the viewport. Defaults to false.
1907 | /// A task that can be awaited to obtain the screenshot as a byte[].
1908 | public async Task CaptureScreenshotAsync(CaptureScreenshotFormat format = CaptureScreenshotFormat.Png, int? quality = null, Viewport viewPort = null, bool fromSurface = true, bool captureBeyondViewport = false)
1909 | {
1910 | ThrowExceptionIfDisposed();
1911 | ThrowExceptionIfBrowserNotInitialized();
1912 |
1913 | if (viewPort != null && viewPort.Scale <= 0)
1914 | {
1915 | throw new ArgumentException($"{nameof(viewPort)}.{nameof(viewPort.Scale)} must be greater than 0.");
1916 | }
1917 |
1918 | using (var devToolsClient = browser.GetDevToolsClient())
1919 | {
1920 | var screenShot = await devToolsClient.Page.CaptureScreenshotAsync(format, quality, viewPort, fromSurface, captureBeyondViewport).ConfigureAwait(continueOnCapturedContext: false);
1921 |
1922 | return screenShot.Data;
1923 | }
1924 | }
1925 |
1926 | ///
1927 | /// Throw exception if browser not initialized.
1928 | ///
1929 | /// Thrown when an exception error condition occurs.
1930 | private void ThrowExceptionIfBrowserNotInitialized()
1931 | {
1932 | if (!InternalIsBrowserInitialized())
1933 | {
1934 | throw new Exception(BrowserNotInitializedExceptionErrorMessage);
1935 | }
1936 | }
1937 |
1938 | ///
1939 | /// Throw exception if disposed.
1940 | ///
1941 | /// Thrown when a supplied object has been disposed.
1942 | private void ThrowExceptionIfDisposed()
1943 | {
1944 | if (IsDisposed)
1945 | {
1946 | throw new ObjectDisposedException("browser", "Browser has been disposed");
1947 | }
1948 | }
1949 |
1950 | private int GetChromiumChildProcessId(string frameIdentifier)
1951 | {
1952 | try
1953 | {
1954 | var parts = frameIdentifier.Split('-');
1955 |
1956 | if (int.TryParse(parts[0], out var childProcessId))
1957 | return childProcessId;
1958 | }
1959 | catch
1960 | {
1961 |
1962 | }
1963 |
1964 | return -1;
1965 | }
1966 | }
1967 | }
1968 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost/FocusHandler.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2020 The CefSharp Authors. All rights reserved.
2 | //
3 | // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4 |
5 | using System.Windows.Input;
6 |
7 | namespace CefSharp.Wpf.HwndHost
8 | {
9 | ///
10 | /// Focus Handler
11 | /// The methods of this class will be called on the CEF UI thread.
12 | ///
13 | public class FocusHandler : IFocusHandler
14 | {
15 | void IFocusHandler.OnGotFocus(IWebBrowser chromiumWebBrowser, IBrowser browser)
16 | {
17 | OnGotFocus(chromiumWebBrowser, browser);
18 | }
19 |
20 | ///
21 | /// Called when the browser component has received focus.
22 | ///
23 | /// the ChromiumWebBrowser control
24 | /// the browser object
25 | protected virtual void OnGotFocus(IWebBrowser chromiumWebBrowser, IBrowser browser)
26 | {
27 |
28 | }
29 |
30 | bool IFocusHandler.OnSetFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, CefFocusSource source)
31 | {
32 | return OnSetFocus(chromiumWebBrowser, browser, source);
33 | }
34 |
35 | ///
36 | /// Called when the browser component is requesting focus.
37 | ///
38 | /// the ChromiumWebBrowser control
39 | /// the browser object, do not keep a reference to this object outside of this method
40 | /// Indicates where the focus request is originating from.
41 | /// Return false to allow the focus to be set or true to cancel setting the focus.
42 | protected virtual bool OnSetFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, CefFocusSource source)
43 | {
44 | //We don't deal with popups as they're rendered by default entirely by CEF
45 | if (browser.IsPopup)
46 | {
47 | return false;
48 | }
49 | // Do not let the browser take focus when a Load method has been called
50 | return source == CefFocusSource.FocusSourceNavigation;
51 | }
52 |
53 |
54 |
55 | void IFocusHandler.OnTakeFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, bool next)
56 | {
57 | OnTakeFocus(chromiumWebBrowser, browser, next);
58 | }
59 |
60 | ///
61 | /// Called when the browser component is about to lose focus.
62 | /// For instance, if focus was on the last HTML element and the user pressed the TAB key.
63 | ///
64 | /// the ChromiumWebBrowser control
65 | /// the browser object
66 | /// Will be true if the browser is giving focus to the next component
67 | /// and false if the browser is giving focus to the previous component.
68 | protected virtual void OnTakeFocus(IWebBrowser chromiumWebBrowser, IBrowser browser, bool next)
69 | {
70 | //We don't deal with popups as they're rendered by default entirely by CEF
71 | if (browser.IsPopup)
72 | {
73 | return;
74 | }
75 |
76 | var hwndChromiumWebBrowser = (ChromiumWebBrowser)chromiumWebBrowser;
77 |
78 | var request = new TraversalRequest(next ? FocusNavigationDirection.Next : FocusNavigationDirection.Previous);
79 |
80 | // NOTE: OnTakeFocus means leaving focus / not taking focus
81 | hwndChromiumWebBrowser.UiThreadRunAsync(() => hwndChromiumWebBrowser.MoveFocus(request));
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost/Handler/IntegratedMessageLoopBrowserProcessHandler.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2022 The CefSharp Authors. All rights reserved.
2 | //
3 | // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4 |
5 | using System;
6 | using System.Timers;
7 | using System.Windows.Threading;
8 | using CefSharp.Handler;
9 |
10 | namespace CefSharp.Wpf.HwndHost.Handler
11 | {
12 | ///
13 | /// A implementation that can be used to
14 | /// integreate CEF into the WPF message loop (Dispatcher).
15 | /// Currently it's a very basic implementation.
16 | /// See the following link for the CEF reference implementation.
17 | /// https://bitbucket.org/chromiumembedded/cef/commits/1ff26aa02a656b3bc9f0712591c92849c5909e04?at=2785
18 | ///
19 | public class IntegratedMessageLoopBrowserProcessHandler
20 | : BrowserProcessHandler
21 | {
22 | ///
23 | /// Sixty Times per second
24 | ///
25 | public const int SixtyTimesPerSecond = 1000 / 60; // 60fps
26 | ///
27 | /// Thirty Times per second
28 | ///
29 | public const int ThirtyTimesPerSecond = 1000 / 30; //30fps
30 |
31 | private DispatcherTimer dispatcherTimer;
32 | private Dispatcher dispatcher;
33 | private readonly DispatcherPriority dispatcherPriority;
34 | private int interval;
35 |
36 | ///
37 | /// Default constructor
38 | ///
39 | /// WPF Dispatcher
40 | /// Priority at which is called using the Dispatcher
41 | /// the in miliseconds (frame rate), for 30/60 times per second use
42 | /// / respectively.
43 | public IntegratedMessageLoopBrowserProcessHandler(Dispatcher dispatcher, DispatcherPriority dispatcherPriority = DispatcherPriority.Render, int interval = ThirtyTimesPerSecond)
44 | {
45 | if (dispatcher == null)
46 | {
47 | throw new ArgumentNullException(nameof(dispatcher));
48 | }
49 |
50 | if (interval < 10)
51 | {
52 | throw new ArgumentOutOfRangeException(nameof(interval), "Argument less than 10. ");
53 | }
54 |
55 | dispatcherTimer = new DispatcherTimer(dispatcherPriority, dispatcher)
56 | {
57 | Interval = TimeSpan.FromMilliseconds(interval)
58 | };
59 |
60 | dispatcherTimer.Tick += OnDispatcherTimerTick;
61 | dispatcherTimer.Start();
62 |
63 | this.dispatcher = dispatcher;
64 | this.dispatcher.ShutdownStarted += DispatcherShutdownStarted;
65 | this.dispatcherPriority = dispatcherPriority;
66 | this.interval = interval;
67 |
68 | Cef.ShutdownStarted += OnCefShutdownStarted;
69 | }
70 |
71 | private void OnCefShutdownStarted(object sender, EventArgs e)
72 | {
73 | InternalDispose();
74 | }
75 |
76 | private void DispatcherShutdownStarted(object sender, EventArgs e)
77 | {
78 | //If the dispatcher is shutting down then we will cleanup
79 | InternalDispose();
80 | }
81 |
82 | private void OnDispatcherTimerTick(object sender, EventArgs e)
83 | {
84 | // Execute Cef.DoMessageLoopWork on the UI Thread
85 | // Typically this would happen 30/60 times per second (frame rate)
86 | Cef.DoMessageLoopWork();
87 | }
88 |
89 | ///
90 | protected override void OnScheduleMessagePumpWork(long delay)
91 | {
92 | // If the delay is greater than the Maximum then use ThirtyTimesPerSecond
93 | // instead - we do this to achieve a minimum number of FPS
94 | if (delay > interval)
95 | {
96 | delay = interval;
97 | }
98 |
99 | // When delay <= 0 we'll execute Cef.DoMessageLoopWork immediately
100 | // if it's greater than we'll just let the Timer which fires 30 times per second
101 | // care of the call
102 | if (delay <= 0)
103 | {
104 | dispatcher?.InvokeAsync(() => Cef.DoMessageLoopWork(), dispatcherPriority);
105 | }
106 | }
107 |
108 | ///
109 | protected override void Dispose(bool disposing)
110 | {
111 | if (disposing)
112 | {
113 | InternalDispose();
114 | }
115 |
116 | base.Dispose(disposing);
117 | }
118 |
119 | private void InternalDispose()
120 | {
121 | if (dispatcher != null)
122 | {
123 | dispatcher.ShutdownStarted -= DispatcherShutdownStarted;
124 | dispatcher = null;
125 | }
126 |
127 | dispatcherTimer?.Stop();
128 | dispatcherTimer = null;
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost/IWpfWebBrowser.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 The CefSharp Authors. All rights reserved.
2 | //
3 | // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4 |
5 | using System.Windows;
6 | using System.Windows.Input;
7 | using System.Windows.Threading;
8 |
9 | namespace CefSharp.Wpf.HwndHost
10 | {
11 | ///
12 | /// WPF specific implementation, has reference to some of the commands
13 | /// and properties the exposes.
14 | ///
15 | ///
16 | public interface IWpfWebBrowser : IWebBrowser, IInputElement
17 | {
18 | ///
19 | /// Navigates to the previous page in the browser history. Will automatically be enabled/disabled depending on the
20 | /// browser state.
21 | ///
22 | /// The back command.
23 | ICommand BackCommand { get; }
24 |
25 | ///
26 | /// Navigates to the next page in the browser history. Will automatically be enabled/disabled depending on the
27 | /// browser state.
28 | ///
29 | /// The forward command.
30 | ICommand ForwardCommand { get; }
31 |
32 | ///
33 | /// Reloads the content of the current page. Will automatically be enabled/disabled depending on the browser state.
34 | ///
35 | /// The reload command.
36 | ICommand ReloadCommand { get; }
37 |
38 | ///
39 | /// Prints the current browser contents.
40 | ///
41 | /// The print command.
42 | ICommand PrintCommand { get; }
43 |
44 | ///
45 | /// Increases the zoom level.
46 | ///
47 | /// The zoom in command.
48 | ICommand ZoomInCommand { get; }
49 |
50 | ///
51 | /// Decreases the zoom level.
52 | ///
53 | /// The zoom out command.
54 | ICommand ZoomOutCommand { get; }
55 |
56 | ///
57 | /// Resets the zoom level to the default. (100%)
58 | ///
59 | /// The zoom reset command.
60 | ICommand ZoomResetCommand { get; }
61 |
62 | ///
63 | /// Opens up a new program window (using the default text editor) where the source code of the currently displayed web
64 | /// page is shown.
65 | ///
66 | /// The view source command.
67 | ICommand ViewSourceCommand { get; }
68 |
69 | ///
70 | /// Command which cleans up the Resources used by the ChromiumWebBrowser
71 | ///
72 | /// The cleanup command.
73 | ICommand CleanupCommand { get; }
74 |
75 | ///
76 | /// Stops loading the current page.
77 | ///
78 | /// The stop command.
79 | ICommand StopCommand { get; }
80 |
81 | ///
82 | /// Cut selected text to the clipboard.
83 | ///
84 | /// The cut command.
85 | ICommand CutCommand { get; }
86 |
87 | ///
88 | /// Copy selected text to the clipboard.
89 | ///
90 | /// The copy command.
91 | ICommand CopyCommand { get; }
92 |
93 | ///
94 | /// Paste text from the clipboard.
95 | ///
96 | /// The paste command.
97 | ICommand PasteCommand { get; }
98 |
99 | ///
100 | /// Select all text.
101 | ///
102 | /// The select all command.
103 | ICommand SelectAllCommand { get; }
104 |
105 | ///
106 | /// Undo last action.
107 | ///
108 | /// The undo command.
109 | ICommand UndoCommand { get; }
110 |
111 | ///
112 | /// Redo last action.
113 | ///
114 | /// The redo command.
115 | ICommand RedoCommand { get; }
116 |
117 | ///
118 | /// Gets the associated with this instance.
119 | ///
120 | /// The dispatcher.
121 | Dispatcher Dispatcher { get; }
122 |
123 | ///
124 | /// The zoom level at which the browser control is currently displaying.
125 | /// Can be set to 0 to clear the zoom level (resets to default zoom level).
126 | ///
127 | /// The zoom level.
128 | double ZoomLevel { get; set; }
129 |
130 | ///
131 | /// The increment at which the property will be incremented/decremented.
132 | ///
133 | /// The zoom level increment.
134 | double ZoomLevelIncrement { get; set; }
135 |
136 | ///
137 | /// The title of the web page being currently displayed.
138 | ///
139 | /// The title.
140 | /// This property is implemented as a Dependency Property and fully supports data binding.
141 | string Title { get; }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost/Internals/DelegateCommand.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 The CefSharp Authors. All rights reserved.
2 | //
3 | // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4 |
5 | using System;
6 | using System.Windows.Input;
7 |
8 | namespace CefSharp.Wpf.HwndHost.Internals
9 | {
10 | ///
11 | /// DelegateCommand
12 | ///
13 | ///
14 | internal class DelegateCommand : ICommand
15 | {
16 | ///
17 | /// The command handler
18 | ///
19 | private readonly Action commandHandler;
20 | ///
21 | /// The can execute handler
22 | ///
23 | private readonly Func canExecuteHandler;
24 |
25 | ///
26 | /// Occurs when changes occur that affect whether or not the command should execute.
27 | ///
28 | public event EventHandler CanExecuteChanged;
29 |
30 | ///
31 | /// Initializes a new instance of the class.
32 | ///
33 | /// The command handler.
34 | /// The can execute handler.
35 | public DelegateCommand(Action commandHandler, Func canExecuteHandler = null)
36 | {
37 | this.commandHandler = commandHandler;
38 | this.canExecuteHandler = canExecuteHandler;
39 | }
40 |
41 | ///
42 | /// Defines the method to be called when the command is invoked.
43 | ///
44 | /// Data used by the command. If the command does not require data to be passed, this object can be set to null.
45 | public void Execute(object parameter)
46 | {
47 | commandHandler();
48 | }
49 |
50 | ///
51 | /// Defines the method that determines whether the command can execute in its current state.
52 | ///
53 | /// Data used by the command. If the command does not require data to be passed, this object can be set to null.
54 | /// true if this command can be executed; otherwise, false.
55 | public bool CanExecute(object parameter)
56 | {
57 | return canExecuteHandler == null || canExecuteHandler();
58 | }
59 |
60 | ///
61 | /// Raises the can execute changed.
62 | ///
63 | public void RaiseCanExecuteChanged()
64 | {
65 | CanExecuteChanged?.Invoke(this, EventArgs.Empty);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/CefSharp.Wpf.HwndHost/Internals/NoCloseLifespanHandler.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2022 The CefSharp Authors. All rights reserved.
2 | //
3 | // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4 |
5 | namespace CefSharp.Wpf.HwndHost.Internals
6 | {
7 | ///
8 | /// LifeSpanHandler used internally
9 | /// - Cancels sending of WM_CLOSE message for main browser
10 | /// - Allows popups to close
11 | ///
12 | public class NoCloseLifespanHandler
13 | : CefSharp.Handler.LifeSpanHandler
14 | {
15 | ///
16 | protected override bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
17 | {
18 | if(browser.IsPopup)
19 | {
20 | return false;
21 | }
22 |
23 | return true;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/CefSharp.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cefsharp/CefSharp.Wpf.HwndHost/99e1e902353a187c83beb940401b40182b3e05d8/CefSharp.snk
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | // Copyright © The CefSharp Authors. All rights reserved.
2 | //
3 | // Redistribution and use in source and binary forms, with or without
4 | // modification, are permitted provided that the following conditions are
5 | // met:
6 | //
7 | // * Redistributions of source code must retain the above copyright
8 | // notice, this list of conditions and the following disclaimer.
9 | //
10 | // * Redistributions in binary form must reproduce the above
11 | // copyright notice, this list of conditions and the following disclaimer
12 | // in the documentation and/or other materials provided with the
13 | // distribution.
14 | //
15 | // * Neither the name of Google Inc. nor the name Chromium Embedded
16 | // Framework nor the name CefSharp nor the names of its contributors
17 | // may be used to endorse or promote products derived from this software
18 | // without specific prior written permission.
19 | //
20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/NuGet.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | [](https://cefsharp.github.io/ "CefSharp - Embedded Chromium for .NET")
3 |
4 | # CefSharp.Wpf.HwndHost
5 | [](https://ci.appveyor.com/project/cefsharp/cefsharp-wpf-hwndhost)
6 | [](https://www.nuget.org/packages/CefSharp.Wpf.HwndHost/)
7 |
8 | Designed as a drop in replacement for [CefSharp.Wpf.ChromiumWebBrowser](http://nuget.org/packages/CefSharp.Wpf/) for those who want the native Win32 based implementation (For IME support and better performance).
9 |
10 | The control uses a [HwndHost](https://docs.microsoft.com/en-us/dotnet/api/system.windows.interop.hwndhost?view=netframework-4.6.2) to host the native `CefBrowser` instance.
11 |
12 | As with any HwndHost based control standard `AirSpace` issues apply.
13 | - https://docs.microsoft.com/en-us/dotnet/desktop/wpf/advanced/wpf-and-win32-interoperation
14 | - https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/technology-regions-overview
15 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | image: Visual Studio 2019
2 |
3 | version: 131.3.50-CI{build}
4 |
5 | clone_depth: 10
6 |
7 | #Skip builing if we only modify text files
8 | skip_commits:
9 | files:
10 | - '**/*.md'
11 | - '**/*.html'
12 | - '**/*.js'
13 |
14 | # build platform, i.e. x86, x64, Any CPU. This setting is optional.
15 | platform: Any CPU
16 |
17 | # build Configuration, i.e. Debug, Release, etc.
18 | configuration: Release
19 |
20 | # scripts to run before build
21 | before_build:
22 | dotnet restore
23 |
24 | build:
25 | project: CefSharp.Wpf.HwndHost.sln
26 | # MSBuild verbosity level
27 | verbosity: normal
28 |
29 | artifacts:
30 | - path: '**\*.nupkg'
31 | name: nupkgfiles
32 |
33 | # Publish to myget.org feed
34 | deploy:
35 | provider: NuGet
36 | server: https://www.myget.org/F/cefsharp/api/v2/package
37 | api_key:
38 | secure: V8du2PPvMPok3Ya701jt5v2XWQgOZf52/H5wDHXBpKvXYkIIe8sonhVUy2TmEkqt
39 | skip_symbols: false
40 | symbol_server: https://www.myget.org/F/cefsharp/api/v2/package
41 | artifact: nupkgfiles
42 | on:
43 | branch: master # release from master branch only
44 | APPVEYOR_REPO_TAG: true # deploy on tag push only
45 |
--------------------------------------------------------------------------------